home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2002 #11 / Amiga Plus CD - 2002 - No. 11.iso / Games / WormWars / Source / system.c < prev    next >
C/C++ Source or Header  |  2002-10-21  |  115KB  |  3,028 lines

  1. /* $Filename: WormWars/Source/system.c $
  2.  * $VER:      Worm Wars 7.21a for Amiga $
  3.  *
  4.  * © Copyright 1993-2002 James R. Jacobs. Freely distributable.
  5.  *        _
  6.  *       //   Only -=AMIGA=- makes it possible
  7.  *      //
  8.  * _   //
  9.  * \\ //
  10.  *  \X/
  11.  
  12. #INCLUDES ---------------------------------------------------------------- */
  13.  
  14. #include "amiga.h"
  15. #include "system.h"
  16.  
  17. // #include <assert.h>
  18. // #define ASSERT
  19.  
  20. /* PROTOTYPES (Amiga-only) ----------------------------------------------- */
  21.  
  22. MODULE void freefx(void);
  23. MODULE void loadthefx(void);
  24. MODULE void loadthemusic(void);
  25. MODULE void parsewb(void);
  26. MODULE void pausetimer(void);
  27. MODULE void unpausetimer(void);
  28. MODULE ABOOL beginfx(void);
  29. MODULE UBYTE ReadJoystick(UWORD joynum);
  30. MODULE ABOOL firebutton(void);
  31.  
  32. IMPORT void align(STRPTR string, SBYTE size, TEXT filler);
  33.  
  34. AGLOBAL struct Window          *HelpWindowPtr = NULL,
  35.                                *MainWindowPtr = NULL;
  36. AGLOBAL struct Menu*            MenuPtr       = NULL;
  37. AGLOBAL struct VisualInfo*      VisualInfoPtr = NULL;
  38. AGLOBAL struct timerequest*     TimerRqPtr    = NULL;
  39. AGLOBAL struct Screen*          ScreenPtr     = NULL;
  40. AGLOBAL ABOOL                   icons         = TRUE;
  41.  
  42. /* EXTERNAL VARIABLES ----------------------------------------------------- */
  43.  
  44. IMPORT  struct ExecBase*      SysBase;
  45.  
  46. IMPORT  ABOOL                 anims, modified, randomflag, turbo, thick;
  47. IMPORT  SBYTE                 a, eachworm[4][2][9],
  48.                               level, levels, players, sourcelevel,
  49.                               startx[MAXLEVELS + 1], starty[MAXLEVELS + 1];
  50. IMPORT  SWORD                 secondsleft, secondsperlevel;
  51. IMPORT  ULONG                 delay, r;
  52. IMPORT  UBYTE                 board[MAXLEVELS + 1][FIELDX + 1][FIELDY + 1],
  53.                               field[FIELDX + 1][FIELDY + 1];
  54. IMPORT  struct HiScoreStruct  hiscore[HISCORES + 1];
  55. IMPORT  struct WormStruct     worm[4];
  56. IMPORT  UBYTE                 missileframes[4][MISSILEFRAMES + 1];
  57. IMPORT  TEXT                  pathname[81],
  58.                               date[DATELENGTH + 1],
  59.                               times[TIMELENGTH + 1];
  60. IMPORT  struct Image          Image;
  61. IMPORT  UWORD                 ImageData[ARRAYSIZE + 1][SQUAREY * DEPTH];
  62.  
  63.         struct Library*       TimerBase;
  64.         struct Library*       ASLBase        = NULL;
  65.         struct DiskFontBase*  DiskFontBase   = NULL;
  66.         struct GadToolsBase*  GadToolsBase   = NULL;
  67.         struct GfxBase*       GfxBase        = NULL;
  68.         struct Library*       IconBase       = NULL;
  69.         struct IntuitionBase* IntuitionBase  = NULL;
  70.         struct LowLevelBase*  LowLevelBase   = NULL;
  71.         struct MEDPlayerBase* MEDPlayerBase  = NULL;
  72.         struct UtilityBase*   UtilityBase    = NULL;
  73.  
  74. /* MODULE VARIABLES (used only within system.c) --------------------------- */
  75.  
  76. MODULE  ABOOL  eversent[4],
  77.                ignore       = FALSE,
  78.                quiet        = FALSE;
  79. MODULE  SBYTE  AudioClosed  = TRUE,
  80.                OldPri       = 0,
  81.                TimerClosed  = TRUE;
  82. MODULE  UBYTE  fxable       = UNTRIED,
  83.                mode         = NULL,
  84.                musicable    = UNTRIED;
  85. MODULE  ULONG  fsize,
  86.                receipter[4] = {(ULONG) -1, (ULONG) -1, (ULONG) -1, (ULONG) -1};
  87. MODULE  UBYTE* fbase        = NULL;
  88. MODULE  BPTR   FilePtr      = NULL;
  89. MODULE  APTR   OldWindowPtr = NULL;
  90. MODULE  SBYTE  hiframe      = -1;
  91. MODULE  UBYTE                 ConfigBuffer[22];
  92. MODULE  UWORD                 DisplayDepth  = DEPTH;
  93. MODULE  ULONG                 DisplayID     = HIRES_KEY | PAL_MONITOR_ID | LACE,
  94.                               DisplayWidth  = ENTIREXPIXEL,
  95.                               DisplayHeight = ENTIREYPIXEL;
  96.  
  97. // MODULE  struct QDPlaneInfo QDPI;
  98. // MODULE  ABOOL              useqdraw = FALSE;
  99.  
  100. MODULE    struct TextAttr       WormWars8 =
  101. {    (STRPTR) "WormWars.font", 8, FS_NORMAL, FPF_DISKFONT | FPF_DESIGNED
  102. },                            Topaz8 =
  103. {    (STRPTR) "topaz.font", 8, FS_NORMAL, FPF_ROMFONT | FPF_DESIGNED
  104. };
  105.  
  106. MODULE    struct Gadget         *CycleGadgetPtr[4]  = {NULL, NULL, NULL, NULL},
  107.                               *GListPtr           = NULL,
  108.                               *PrevGadgetPtr      = NULL,
  109.                               *RandomGadgetPtr    = NULL,
  110.                               *FEGadgetPtr        = NULL,
  111.                               *PlayGadgetPtr      = NULL,
  112.                               *StringGadgetPtr[5] = {NULL, NULL, NULL, NULL, NULL};
  113. MODULE    struct MsgPort        *AudioPortPtr[4]    = {NULL, NULL, NULL, NULL},
  114.                               *TimerPortPtr       = NULL;
  115. MODULE    struct timeval        *CurrentValPtr      = NULL,
  116.                               *PausedValPtr       = NULL,
  117.                               *StartValPtr        = NULL;
  118.  
  119. MODULE  struct CIA*           CIAPtr              = (struct CIA *) 0xBFE001;
  120. MODULE    struct RDArgs*        ArgsPtr             = NULL;
  121. MODULE    struct FileRequester* ASLRqPtr            = NULL;
  122. MODULE    struct IOAudio*       AudioRqPtr[4]       = {NULL, NULL, NULL, NULL};
  123. MODULE    struct TextFont*      FontPtr             = NULL;
  124. MODULE    struct Process*       ProcessPtr          = NULL;
  125. MODULE    struct MMD0*          SongPtr             = NULL;
  126. MODULE    struct WBArg*         WBArg               = NULL;
  127. MODULE    struct WBStartup*     WBMsg               = NULL;
  128.  
  129. #define CENTREXPIXEL (STARTXPIXEL + ((ENDXPIXEL - STARTXPIXEL) / 2))
  130. #define CENTREYPIXEL (STARTYPIXEL + ((ENDYPIXEL - STARTYPIXEL) / 2))
  131. #define RUNDOWNX     234
  132. #define RUNDOWNY      96
  133. #define RUNDOWNY_2ND  (SQUAREY * 11)
  134. #define RUNDOWNLENGTH 27
  135. #define RUNDOWNX_2ND  (FONTX * 14)
  136. #define RUNDOWNX_3RD  (FONTX * 21)
  137. #define RUNDOWNX_4TH  (FONTX * 28)
  138.  
  139. /* FUNCTIONS -------------------------------------------------------------- */
  140.  
  141. int main(int argc, char** argv)
  142. {
  143. BPTR                        FileHandle,
  144.                             OldDir;
  145. SBYTE                       error    = 0,
  146.                             player,
  147.                             which;
  148. TEXT                        saystring[SAYLIMIT + 1];
  149. SWORD                       oldsecondsleft;
  150. SLONG                       i,
  151.                             args[12] = {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L};
  152. struct ScreenModeRequester* smr;
  153. ABOOL                       ok;
  154. UWORD   Pens[10] =
  155. {       BLACK,     /* DETAILPEN            text in title bar */
  156.         WHITE,     /* BLOCKPEN             fill title bar */
  157.         BLACK,     /* TEXTPEN              regular text on BACKGROUNDPEN */
  158.         LIGHTGREY, /* SHINEPEN             bright edge */
  159.         DARKGREY,  /* SHADOWPEN            dark edge */
  160.         BLUE,      /* FILLPEN              filling active window borders
  161.                                            and selected gadgets */
  162.         BLACK,     /* FILLTEXTPEN          text rendered over FILLPEN */
  163.         MEDIUMGREY,/* BACKGROUNDPEN        background colour */
  164.         RED,       /* HIGHLIGHTTEXTPEN     highlighted text on BACKGROUNDPEN */
  165.         (UWORD) ~0 /* and used against BLOCKPEN in ASL save requesters */
  166. };
  167. struct ColorSpec        Colours[21] =
  168. {   { 0, 0x0, 0x0, 0x0}, // black
  169.     { 1, 0xA, 0xA, 0xA}, // light grey
  170.     { 2, 0x3, 0x6, 0x3}, // dark green
  171.     { 3, 0x6, 0x6, 0x3}, // dark yellow
  172.     { 4, 0x5, 0x5, 0x5}, // medium grey
  173.     { 5, 0x4, 0x4, 0xA}, // dark blue
  174.     { 6, 0x8, 0x8, 0xF}, // light blue
  175.     { 7, 0x7, 0xB, 0x7}, // light green
  176.     { 8, 0x9, 0x3, 0x3}, // dark red
  177.     { 9, 0x8, 0x4, 0x2}, // brown
  178.     {10, 0xF, 0x5, 0x5}, // light red
  179.     {11, 0xB, 0xB, 0x6}, // light yellow
  180.     {12, 0x2, 0x2, 0x2}, // dark grey
  181.     {13, 0xF, 0x9, 0x8}, // orange
  182.     {14, 0xF, 0x8, 0xF}, // purple
  183.     {15, 0xF, 0xF, 0xF}, // white
  184.     {16, 0x0, 0x0, 0x0}, /* pointer: transparent */
  185.     {17, 0xE, 0x4, 0x4}, /* pointer: fill */
  186.     {18, 0x3, 0x3, 0x3}, /* pointer: shadow */
  187.     {19, 0xC, 0xC, 0xC}, /* pointer: shine */
  188.     {-1, NULL,NULL,NULL}
  189. };
  190.  
  191.     // Start of program.
  192.  
  193.     // version embedding into executable
  194.     if (0) /* that is, never */
  195.     {   say(VERSION, ANYTHING);
  196.     }
  197.  
  198.     if (!(IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37L)))
  199.     {   Write(Output(), OLDKICKSTART, strlen(OLDKICKSTART));
  200.         cleanexit(EXIT_FAILURE);
  201.     }
  202.  
  203.     /* From this point onwards, we can be sure we have Kickstart 2.04+... */
  204.  
  205.     for (i = 0; i <= SAMPLES; i++)
  206.     {   samp[i].base = NULL;
  207.     }
  208.     enginesetup();
  209.     ProcessPtr = (struct Process *) FindTask(NULL);
  210.  
  211.     if (SysBase->LibNode.lib_Version < 36L)
  212.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Need exec.library V36+!\0", 24);
  213.     cleanexit(EXIT_FAILURE);
  214.     }
  215.     if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 0L)))
  216.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open graphics.library!\0", 24);
  217.     cleanexit(EXIT_FAILURE);
  218.     }
  219.     if (!(GadToolsBase = (struct GadToolsBase *) OpenLibrary("gadtools.library", 37L)))
  220.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open GadTools.library V37+!\0", 24);
  221.     cleanexit(EXIT_FAILURE);
  222.     }
  223.     if (!(ASLBase = (struct ASLBase *) OpenLibrary("asl.library", 0L)))
  224.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open ASL.library!\0", 24);
  225.     cleanexit(EXIT_FAILURE);
  226.     }
  227.     if (!(UtilityBase = (struct UtilityBase *) OpenLibrary("utility.library", 0L)))
  228.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open utility.library!\0", 24);
  229.     cleanexit(EXIT_FAILURE);
  230.     }
  231.     if (!(IconBase = (struct Library *) OpenLibrary("icon.library", 0L)))
  232.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open icon.library!\0", 24);
  233.         cleanexit(EXIT_FAILURE);
  234.     }
  235.     LowLevelBase = (struct Library *) OpenLibrary("lowlevel.library", 0L);
  236.  
  237.     ok = FALSE;
  238.     if (FileHandle = Open("PROGDIR:WormWars.config", MODE_OLDFILE))
  239.     {   if (Read(FileHandle, ConfigBuffer, 22) == 22)
  240.         {   ok = TRUE;
  241.             for (player = 0; player <= 3; player++)
  242.             {   worm[player].control = (UBYTE) ConfigBuffer[player];
  243.             }
  244.             DisplayID     = (ULONG) (  (ConfigBuffer[ 4] * 16777216)
  245.                                      + (ConfigBuffer[ 5] *    65536)
  246.                                      + (ConfigBuffer[ 6] *      256)
  247.                                      +  ConfigBuffer[ 7]            );
  248.             DisplayWidth  = (ULONG) (  (ConfigBuffer[ 8] * 16777216)
  249.                                      + (ConfigBuffer[ 9] *    65536)
  250.                                      + (ConfigBuffer[10] *      256)
  251.                                      +  ConfigBuffer[11]            );
  252.             DisplayHeight = (ULONG) (  (ConfigBuffer[12] * 16777216)
  253.                                      + (ConfigBuffer[13] *    65536)
  254.                                      + (ConfigBuffer[14] *      256)
  255.                                      +  ConfigBuffer[15]            );
  256.             DisplayDepth  = (UWORD) (  (ConfigBuffer[16] *      256)
  257.                                      +  ConfigBuffer[17]            );
  258.             randomflag    = (ABOOL)     ConfigBuffer[18];
  259.             icons         = (ABOOL)     ConfigBuffer[19];
  260.             thick         = (ABOOL)     ConfigBuffer[20];
  261.             anims         = (ABOOL)     ConfigBuffer[21];
  262.         }
  263.         Close(FileHandle);
  264.         // FileHandle = NULL;
  265.     }
  266.  
  267. /* argument parsing */
  268.  
  269. if (argc) /* started from CLI */
  270. {   if (!(ArgsPtr = ReadArgs
  271.     (   "-N=NOPRELOAD/S,-A=NOANIMS/S,-I=NOICONS/S,-P=PRI/K/N,"
  272.         "-S=SHUFFLE/S,-Q=QUIET/S,GREEN/K,RED/K,BLUE/K,YELLOW/K,"
  273.         "-T=THICKTAILS,FILE",
  274.         (LONG *) args,
  275.         NULL
  276.     )))
  277.     {   Printf
  278.         (   "Usage: %s [-n=NOPRELOAD] [-a=NOANIMS] [-i=NOICONS] "
  279.             "[-p=PRI <priority>] [-s=SHUFFLE] [-q=QUIET] "
  280.             "[GREEN=KYBD|JOY|GAMEPAD|AMIGA|NONE] "
  281.             "[RED=KYBD|JOY|GAMEPAD|AMIGA|NONE] [BLUE=JOY|GAMEPAD|AMIGA|NONE] "
  282.             "[YELLOW=JOY|GAMEPAD|AMIGA|NONE] [[FILE=]<fieldset>]\n",
  283.             argv[0]
  284.         );
  285.         cleanexit(EXIT_FAILURE);
  286.     }
  287.     if (args[0])
  288.     {   fxable = musicable = DEFER;
  289.     }
  290.     if (args[1])
  291.     {   anims = FALSE;
  292.     }
  293.     if (args[2])
  294.     {   icons = FALSE;
  295.     }
  296.     if (args[3])
  297.     {   if (args[3] < -128 || args[3] > 5)
  298.         {   Printf("%s: Priority range is -128 to +5\n", argv[0]);
  299.             cleanexit(EXIT_FAILURE);
  300.         }
  301.         OldPri = SetTaskPri((struct Task *) ProcessPtr, args[3]);
  302.     }
  303.     if (args[4])
  304.     {   randomflag = TRUE;
  305.     }
  306.     if (args[5])
  307.     {   quiet = TRUE;
  308.     }
  309.     if (args[6])
  310.     {   if (!stricmp(args[6], "KYBD"))
  311.         {   worm[0].control = KEYBOARD;
  312.         } elif (!stricmp(args[6], "JOY"))
  313.         {   worm[0].control = JOYSTICK;
  314.         } elif (!stricmp(args[6], "GAMEPAD"))
  315.         {   if (!LowLevelBase)
  316.             {   Printf("%s: Can't open lowlevel.library!\n", argv[0]);
  317.                 cleanexit(EXIT_FAILURE);
  318.             }
  319.             worm[0].control = GAMEPAD;
  320.         } elif (!stricmp(args[6], "AMIGA"))
  321.         {   worm[0].control = AMIGA;
  322.         } elif (!stricmp(args[6], "NONE"))
  323.         {   worm[0].control = NONE;
  324.         } else
  325.         {   Printf("%s: Green worm control must be KYBD, JOY, GAMEPAD, AMIGA or NONE\n", argv[0]);
  326.             cleanexit(EXIT_FAILURE);
  327.     }   }
  328.     if (args[7])
  329.     {   if (!stricmp(args[7], "KYBD"))
  330.         {   worm[1].control = KEYBOARD;
  331.         } elif (!stricmp(args[7], "JOY"))
  332.         {   worm[1].control = JOYSTICK;
  333.         } elif (!stricmp(args[7], "GAMEPAD"))
  334.         {   if (!LowLevelBase)
  335.             {   Printf("%s: Can't open lowlevel.library!\n", argv[0]);
  336.                 cleanexit(EXIT_FAILURE);
  337.             }
  338.             worm[1].control = GAMEPAD;
  339.         } elif (!stricmp(args[7], "AMIGA"))
  340.         {   worm[1].control = AMIGA;
  341.         } elif (!stricmp(args[7], "NONE"))
  342.         {   worm[1].control = NONE;
  343.         } else
  344.         {   Printf("%s: Red worm control must be KYBD, JOY, GAMEPAD, AMIGA or NONE\n", argv[0]);
  345.             cleanexit(EXIT_FAILURE);
  346.     }   }
  347.     if (args[8])
  348.     {   if (!stricmp(args[8], "JOY"))
  349.         {   worm[2].control = JOYSTICK;
  350.         } elif (!stricmp(args[8], "GAMEPAD"))
  351.         {   if (!LowLevelBase)
  352.             {   Printf("%s: Can't open lowlevel.library!\n", argv[0]);
  353.                 cleanexit(EXIT_FAILURE);
  354.             }
  355.             worm[2].control = GAMEPAD;
  356.         } elif (!stricmp(args[8], "AMIGA"))
  357.         {   worm[2].control = AMIGA;
  358.         } elif (!stricmp(args[8], "NONE"))
  359.         {   worm[2].control = NONE;
  360.         } else
  361.         {   Printf("%s: Blue worm control must be JOY, GAMEPAD, AMIGA or NONE\n", argv[0]);
  362.             cleanexit(EXIT_FAILURE);
  363.     }   }
  364.     if (args[9])
  365.     {   if (!stricmp(args[9], "JOY"))
  366.         {   worm[3].control = JOYSTICK;
  367.         } elif (!stricmp(args[9], "GAMEPAD"))
  368.         {   if (!LowLevelBase)
  369.             {   Printf("%s: Can't open lowlevel.library!\n", argv[0]);
  370.                 cleanexit(EXIT_FAILURE);
  371.             }
  372.             worm[3].control = GAMEPAD;
  373.         } elif (!stricmp(args[9], "AMIGA"))
  374.         {   worm[3].control = AMIGA;
  375.         } elif (!stricmp(args[9], "NONE"))
  376.         {   worm[3].control = NONE;
  377.         } else
  378.         {   Printf("%s: Yellow worm control must be JOY, GAMEPAD, AMIGA or NONE\n", argv[0]);
  379.             cleanexit(EXIT_FAILURE);
  380.     }   }
  381.     if (args[10])
  382.     {   thick = TRUE;
  383.     }
  384.     if (args[11])
  385.     {   strcpy(pathname, args[11]);
  386. }   }
  387. else /* started from WB */
  388. {   WBMsg = (struct WBStartup *) argv;
  389.     WBArg = WBMsg->sm_ArgList; /* head of the arg list */
  390.  
  391.     for (i = 0;
  392.          i < WBMsg->sm_NumArgs;
  393.          i++, WBArg++)
  394.     {   if (WBArg->wa_Lock)
  395.         {    /* something that does not support locks */
  396.              parsewb();
  397.         }
  398.         else
  399.         {    /* locks supported, change to the proper directory */
  400.              OldDir = CurrentDir(WBArg->wa_Lock);
  401.              parsewb();
  402.              CurrentDir(OldDir);
  403.         }
  404.         if (i == 1)
  405.         {    strcpy(pathname, WBArg->wa_Name);
  406. }   }   }
  407.  
  408. if (!(DiskFontBase = (struct DiskFontBase *) OpenLibrary("diskfont.library", 0L)))
  409.     error = 1;
  410.     
  411. if
  412. (       (!(StartValPtr = (struct timeval *) AllocMem(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR)))
  413. ||      (!(CurrentValPtr = (struct timeval *) AllocMem(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR)))
  414. ||      (!(PausedValPtr = (struct timeval *) AllocMem(sizeof(struct timeval), MEMF_PUBLIC | MEMF_CLEAR)))
  415. )
  416. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't allocate timer value structure!\0", 24);
  417.     cleanexit(EXIT_FAILURE);
  418. }
  419. if (!(TimerPortPtr = (struct MsgPort *) CreateMsgPort()))
  420. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't allocate timer message port!\0", 24);
  421.     cleanexit(EXIT_FAILURE);
  422. }
  423. if (!(TimerRqPtr = (struct timerequest *) CreateIORequest(TimerPortPtr, sizeof(struct timerequest))))
  424. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create timer I/O request!\0", 24);
  425.     cleanexit(EXIT_FAILURE);
  426. }
  427. if (TimerClosed = OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *) TimerRqPtr, 0))
  428. {    DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open timer.device!\0", 24);
  429.     cleanexit(EXIT_FAILURE);
  430. }
  431. TimerBase = (struct Library *) TimerRqPtr->tr_node.io_Device;
  432.  
  433.     if (!ok && ASLBase->lib_Version >= 38L)
  434.     {   if (!(smr = (struct ScreenModeRequester *) AllocAslRequestTags
  435.         (   ASL_ScreenModeRequest,
  436.             ASLSM_TitleText,            "Worm Wars: Screen Mode Requester",
  437.             ASLSM_InitialDisplayID,     HIRES_KEY | PAL_MONITOR_ID | LACE,
  438.             ASLSM_InitialDisplayWidth,  ENTIREXPIXEL,
  439.             ASLSM_InitialDisplayHeight, ENTIREYPIXEL,
  440.             ASLSM_InitialDisplayDepth,  DEPTH,
  441.             ASLSM_DoWidth,              TRUE,
  442.             ASLSM_DoHeight,             TRUE,
  443.             ASLSM_DoDepth,              TRUE,
  444.             ASLSM_MinWidth,             ENTIREXPIXEL,
  445.             ASLSM_MinHeight,            ENTIREYPIXEL,
  446.             ASLSM_MinDepth,             DEPTH,
  447.             TAG_DONE
  448.         )))
  449.         {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create ASL screen mode request!\0", 24);
  450.             cleanexit(EXIT_FAILURE);
  451.         }
  452.         if (AslRequest(smr, 0L))
  453.         {   DisplayID     = smr->sm_DisplayID;
  454.             DisplayWidth  = smr->sm_DisplayWidth;
  455.             DisplayHeight = smr->sm_DisplayHeight;
  456.             DisplayDepth  = smr->sm_DisplayDepth;
  457.         }
  458.         FreeAslRequest(smr);
  459.     }
  460.  
  461. /* PREPARE DISPLAY -----------------------------------------------
  462.  
  463. font and screen */
  464.  
  465. if ((!error) && (FontPtr = (struct TextFont *) OpenDiskFont(&WormWars8)))
  466. {   /* loaded WormWars.font */
  467.  
  468.     ScreenPtr = (struct Screen *) OpenScreenTags
  469.     (   NULL,
  470.         SA_Width,       DisplayWidth,
  471.         SA_Height,      DisplayHeight,
  472.         SA_Depth,       DisplayDepth,
  473.         SA_DisplayID,   DisplayID,
  474.         SA_Title,       TITLEBAR,
  475.         SA_Colors,      Colours,
  476.         SA_Font,        &WormWars8,
  477.         SA_Pens,        Pens,
  478.         SA_PubName,     "WORMWARS",
  479.         TAG_DONE
  480.     );
  481.     if (!ScreenPtr)
  482.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open screen!\0", 24);
  483.         cleanexit(EXIT_FAILURE);
  484. }   }
  485. else
  486. {   if (!error)
  487.         ; // error = 2;
  488.  
  489.     ScreenPtr = (struct Screen *) OpenScreenTags
  490.     (   NULL,
  491.         SA_Width,       DisplayWidth,
  492.         SA_Height,      DisplayHeight,
  493.         SA_Depth,       DisplayDepth,
  494.         SA_DisplayID,   DisplayID,
  495.         SA_Title,       TITLEBAR,
  496.         SA_Colors,      Colours,
  497.         SA_Font,        &Topaz8,
  498.         SA_Pens,        Pens,
  499.         SA_PubName,     "WORMWARS",
  500.         TAG_DONE
  501.     );
  502.     if (!ScreenPtr)
  503.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open screen nor font!\0", 24);
  504.         cleanexit(EXIT_FAILURE);
  505. }   }
  506.  
  507.     PubScreenStatus(ScreenPtr, NULL); // take the screen public
  508.  
  509.     /* For some reason, we lose 4 memory chunks around this point: 2 chunks
  510.     each of 292 bytes and 10 bytes. It is related to OpenScreenTagList(),
  511.     but is not related to the SA_Colors, SA_Font or SA_Pens tags, nor to
  512.     the timer.device. Perhaps it is due to the public nature of the screen?
  513.  
  514.     Now that the screen is set up, we initialize the turbo graphics as
  515.     follows: */
  516.  
  517. /*  for (i = 0; i < DEPTH; i++)
  518.         QDPI.pi_Planes[i] = ScreenPtr->RastPort.BitMap->Planes[i];
  519.     QDPI.pi_BPR           = ScreenPtr->RastPort.BitMap->BytesPerRow;
  520.     QDPI.pi_PlaneSZ       = ScreenPtr->RastPort.BitMap->BytesPerRow *
  521.                             ScreenPtr->RastPort.BitMap->Rows;
  522. */
  523.  
  524.     // These must be done before the menus are set up
  525.     if (anims)
  526.     {   NewMenu[INDEX_ANIMATIONS].nm_Flags |= CHECKED;
  527.     }
  528.     if (icons)
  529.     {   NewMenu[INDEX_CREATEICONS].nm_Flags |= CHECKED;
  530.     }
  531.     if (thick)
  532.     {   NewMenu[INDEX_THICKTAILS].nm_Flags |= CHECKED;
  533.     }
  534.  
  535.     /* GadTools */
  536.     if
  537.     (   !(   CycleGadget.ng_VisualInfo
  538.           = StringGadget.ng_VisualInfo
  539.           = RandomGadget.ng_VisualInfo
  540.           =     FEGadget.ng_VisualInfo
  541.           =   PlayGadget.ng_VisualInfo
  542.           = VisualInfoPtr = (APTR) GetVisualInfo(ScreenPtr, TAG_DONE)
  543.     )   )
  544.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't get GadTools visual info!\0", 24);
  545.     cleanexit(EXIT_FAILURE);
  546.     }
  547.     if (!(MenuPtr = (struct Menu *) CreateMenus(NewMenu, TAG_DONE)))
  548.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create menus!\0", 24);
  549.         cleanexit(EXIT_FAILURE);
  550.     }
  551.     if (!(LayoutMenus(MenuPtr, VisualInfoPtr, TAG_DONE)))
  552.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't lay out menus!\0", 24);
  553.     cleanexit(EXIT_FAILURE);
  554.     }
  555.     if (!(PrevGadgetPtr = (struct Gadget *) CreateContext(&GListPtr)))
  556.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create GadTools context!\0", 24);
  557.     cleanexit(EXIT_FAILURE);
  558.     }
  559.  
  560.     for (player = 0; player <= 3; player++)
  561.     {   CycleGadget.ng_TopEdge = TSOFFSET + 200 + (player * (FONTX + 8));
  562.     CycleGadgetPtr[player] = PrevGadgetPtr = (struct Gadget *) CreateGadget
  563.         (   CYCLE_KIND,
  564.             PrevGadgetPtr,
  565.             &CycleGadget,
  566.             GTCY_Labels, CycleOptions[player],
  567.             GTCY_Active, worm[player].control,
  568.             GT_Underscore, '_',
  569.             GA_Disabled, TRUE,
  570.             TAG_DONE
  571.     );
  572.     }
  573.     RandomGadgetPtr = PrevGadgetPtr = (struct Gadget *) CreateGadget
  574.     (   CHECKBOX_KIND,
  575.     PrevGadgetPtr,
  576.     &RandomGadget,
  577.     GTCB_Checked, randomflag,
  578.     GT_Underscore, '_',
  579.     GA_Disabled, TRUE,
  580.     TAG_DONE
  581.     );
  582.  
  583.     PlayGadgetPtr = PrevGadgetPtr = (struct Gadget *) CreateGadget
  584.     (   BUTTON_KIND,
  585.     PrevGadgetPtr,
  586.         &PlayGadget,
  587.     GA_Disabled, TRUE,
  588.     TAG_DONE
  589.     );
  590.     FEGadgetPtr = PrevGadgetPtr = (struct Gadget *) CreateGadget
  591.     (   BUTTON_KIND,
  592.     PrevGadgetPtr,
  593.         &FEGadget,
  594.     GA_Disabled, TRUE,
  595.     TAG_DONE
  596.     );
  597.  
  598.     /* main window */
  599.     if (!(MainWindowPtr = (struct Window *) OpenWindowTags(NULL,
  600.         WA_Top,                 11,
  601.         WA_Width,               SCREENXPIXEL,
  602.         WA_Height,              SCREENYPIXEL,
  603.         WA_IDCMP,               IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS | IDCMP_CLOSEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_MENUPICK | IDCMP_MENUVERIFY | CYCLEIDCMP | STRINGIDCMP | CHECKBOXIDCMP | IDCMP_REFRESHWINDOW | IDCMP_INTUITICKS,
  604.         WA_Gadgets,             GListPtr,
  605.     WA_CustomScreen,    ScreenPtr,
  606.     WA_Borderless,        TRUE,
  607.     WA_Activate,        TRUE,
  608.     WA_SmartRefresh,    TRUE,
  609.         WA_ReportMouse,         TRUE,
  610.     WA_RptQueue,        16,
  611.     TAG_DONE)))
  612.         {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't open main window!\0", 24);
  613.             cleanexit(EXIT_FAILURE);
  614.     }
  615.    
  616.     /* redirection of AmigaDOS system requesters */
  617.     OldWindowPtr = ProcessPtr->pr_WindowPtr;
  618.     ProcessPtr->pr_WindowPtr = (APTR) MainWindowPtr;
  619.  
  620.     if (!(ASLRqPtr = AllocAslRequestTags(ASL_FileRequest, ASL_Pattern, PATTERN, ASL_Window, MainWindowPtr, TAG_DONE)))
  621.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create ASL request!\0", 24);
  622.         cleanexit(EXIT_FAILURE);
  623.     }
  624.  
  625.     /* String gadgets: first the window is opened with the cycle and
  626.     checkbox gadgets.
  627.       It is necessary not to display the string gadgets yet, so the
  628.     gadgets are then created and added to the gadget list. You will note
  629.     there is no command given to render them as yet. When the attributes
  630.     are modified at highscore time, the applicable gadget is refreshed
  631.     then. */
  632.  
  633.     for (which = 0; which <= HISCORES; which++)
  634.     {   StringGadget.ng_TopEdge = TSOFFSET + 4 + (which * HISCOREDISTANCE);
  635.         StringGadgetPtr[which] = PrevGadgetPtr = (struct Gadget *) CreateGadget(STRING_KIND, PrevGadgetPtr, &StringGadget, GTST_MaxChars, NAMELENGTH, STRINGA_ReplaceMode, TRUE, GA_Disabled, TRUE, TAG_DONE);
  636.     }
  637.     if (!PrevGadgetPtr)
  638.     {   DisplayAlert(AT_Recovery, "\0\20\20Worm Wars: Can't create GadTools gadgets!\0", 24);
  639.     cleanexit(EXIT_FAILURE);
  640.     }
  641.  
  642.     if (musicable == UNTRIED)
  643.         loadthemusic();
  644.     if (fxable == UNTRIED)
  645.         loadthefx();
  646.     if (fxable == SUCCEEDED)
  647.         toggle(F);
  648.     else if (musicable == SUCCEEDED)
  649.         toggle(M);
  650.  
  651.     strcpy(saystring, "Loading ");
  652.     strcat(saystring, pathname);
  653.     strcat(saystring, "...");
  654.     say(saystring, WHITE);
  655.     if (loadfields(pathname))
  656.     {   if (!quiet)
  657.         {   strcpy(saystring, "Can't open ");
  658.             strcat(saystring, pathname);
  659.             strcat(saystring, "!");
  660.             say(saystring, RED);
  661.         }
  662.         newfields();
  663.         if (!quiet)
  664.         {   anykey(TRUE);
  665.     }   }
  666.  
  667.     while (1)
  668.     {   titlescreen();
  669.         for (i = 0; i <= 3; i++)
  670.         {   if (worm[i].control == GAMEPAD)
  671.             {   SetJoyPortAttrs(worm[i].port, SJA_Type, SJA_TYPE_GAMECTLR, TAG_DONE);
  672.         }   }
  673.     
  674.         /* MAIN GAME LOOP ------------------------------------------------- */
  675.     
  676.         while (a == PLAYGAME)
  677.         {   r++;
  678.             oldsecondsleft = secondsleft;
  679.             GetSysTime(CurrentValPtr);
  680.             SubTime(CurrentValPtr, StartValPtr);
  681.             secondsleft = secondsperlevel - CurrentValPtr->tv_secs;
  682.             if (secondsleft != oldsecondsleft)
  683.                 timeloop();
  684.             if (!turbo)
  685.             {   TimerRqPtr->tr_node.io_Command  = TR_ADDREQUEST;
  686.                 TimerRqPtr->tr_time.tv_secs     = 0;
  687.                 TimerRqPtr->tr_time.tv_micro    = delay;
  688.                 SendIO((struct IORequest *) TimerRqPtr);
  689.             }
  690.             gameloop();
  691.             // CheckIO() returns FALSE if complete, pointer if incomplete.
  692.             if (CheckIO((struct IORequest *) TimerRqPtr)) // CheckIO returns pointer to struct IORequest
  693.                 draw(CLOCKICON, ICONY, CLOCK);
  694.             else draw(CLOCKICON, ICONY, BLACKENED);
  695.             WaitIO((struct IORequest *) TimerRqPtr);
  696.         }
  697.         say("Title Screen", WHITE);
  698. }   }
  699.     
  700. /* SUPPORT FUNCTIONS ----------------------------------------------------- */
  701.     
  702. /* NAME     anykey -- wait for a user press
  703. SYNOPSIS    anykey(ABOOL);
  704. FUNCTION    Waits for a user response. Optional automatic timeout.
  705. INPUTS      timeout - timeout?
  706. RESULTS     FALSE if user presses Escape, TRUE otherwise.
  707. FILE        system.c */
  708.  
  709. ABOOL anykey(ABOOL timeout)
  710. {   ABOOL                done       = FALSE;
  711.     SBYTE                count      = 0;
  712.     UWORD                code, qual;
  713.     ULONG                class;
  714.     struct IntuiMessage* MsgPtr;
  715.  
  716.     clearkybd();
  717.     Forbid();
  718.     MainWindowPtr->Flags |= WFLG_RMBTRAP;
  719.     Permit();
  720.     
  721.         while (!done && !firebutton())
  722.         {   while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
  723.             {   class = MsgPtr->Class;
  724.                 code  = MsgPtr->Code;
  725.                 qual  = MsgPtr->Qualifier;
  726.                 GT_ReplyIMsg(MsgPtr);
  727.                 switch(class)
  728.                 {
  729.                 case IDCMP_RAWKEY:
  730.                     if ((!(qual & IEQUALIFIER_REPEAT)) && code < KEYUP && (code < FIRSTQUALIFIER || code > LASTQUALIFIER))
  731.                     {   done = TRUE;
  732.                         if (code == M)
  733.                             toggle(M);
  734.                         elif (code == F)
  735.                             toggle(F);
  736.                         elif (code == ESCAPE)
  737.                         {   if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  738.                             {   if (verify())
  739.                                 {   cleanexit(EXIT_SUCCESS);
  740.                                 } else
  741.                                 {   Forbid();
  742.                                     MainWindowPtr->Flags &= ~WFLG_RMBTRAP;
  743.                                     Permit();
  744.                                     return(FALSE);
  745.                     }   }   }   }
  746.                 break;
  747.                 case IDCMP_CLOSEWINDOW:
  748.                     cleanexit(EXIT_SUCCESS);
  749.                 break;
  750.                 case IDCMP_ACTIVEWINDOW:
  751.                     ignore = TRUE;
  752.                 break;
  753.                 case IDCMP_MOUSEBUTTONS:
  754.                     if ((code == SELECTDOWN || code == MENUDOWN) && !(qual & IEQUALIFIER_REPEAT))
  755.                         if (ignore)
  756.                             ignore = FALSE;
  757.                         else done = TRUE;
  758.                 break;
  759.                 case IDCMP_INTUITICKS:
  760.                     if (timeout && ++count > PATIENCE)
  761.                         done = TRUE;
  762.                 break;
  763.                 default:
  764.                 break;
  765.         }   }   }
  766.  
  767.     Forbid();
  768.     MainWindowPtr->Flags &= ~WFLG_RMBTRAP;
  769.     Permit();
  770.         return TRUE;
  771. }
  772.  
  773. void celebrate(void)
  774. {   ABOOL                done = FALSE;
  775.     ULONG                class;
  776.     UWORD                code, qual;
  777.     struct IntuiMessage* MsgPtr;
  778.     UBYTE                player;
  779.  
  780.     for (player = 0; player <= 3; player++)
  781.         if (worm[player].control != NONE && worm[player].score >= worm[player].hiscore)
  782.              worm[player].hiscore = worm[player].score;
  783.     waitasec();
  784.     clearkybd();
  785.     while (!done && !firebutton)
  786.     {   while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
  787.         {   class = MsgPtr->Class;
  788.             code  = MsgPtr->Code;
  789.             qual  = MsgPtr->Qualifier;
  790.             GT_ReplyIMsg(MsgPtr);
  791.             switch (class)
  792.             {
  793.             case IDCMP_RAWKEY:
  794.                 if (code == ESCAPE)
  795.                 {   if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  796.                     {   if (verify())
  797.                         {   cleanexit(EXIT_SUCCESS);
  798.                     }   }
  799.                     else
  800.                     {   done = TRUE;
  801.                 }   }
  802.                 elif (code == RETURN || code == ENTER || code == SPACEBAR)
  803.                 {   done = TRUE;
  804.                 } elif (code == M)
  805.                 {   toggle(M);
  806.                 } elif (code == F)
  807.                 {   toggle(F);
  808.                 }
  809.             break;
  810.             case IDCMP_MOUSEBUTTONS:
  811.                 if (code == SELECTDOWN && !(qual & IEQUALIFIER_REPEAT))
  812.                 {   if (ignore)
  813.                     {   ignore = FALSE;
  814.                     }
  815.                     else
  816.                     {   done = TRUE;
  817.                 }   }
  818.             break;
  819.             case IDCMP_ACTIVEWINDOW:
  820.                 ignore = TRUE;
  821.             break;
  822.             case IDCMP_CLOSEWINDOW:
  823.                 cleanexit(EXIT_SUCCESS);
  824.             break;
  825.             default:
  826.             break;
  827.         }   }
  828.         SetRGB4(&ScreenPtr->ViewPort, 17, rand() % 16, rand() % 16, rand() % 16);
  829.         draw(rand() % (FIELDX + 1), rand() % (FIELDY + 1), rand() % LASTOBJECT);
  830.     }
  831.     SetRGB4(&ScreenPtr->ViewPort, 17, 14, 4, 4);
  832.     a = GAMEOVER;
  833. }
  834.  
  835. void cleanexit(SLONG rc)
  836. {   SBYTE i;
  837.     BPTR  FileHandle;
  838.  
  839. // CheckIO() returns NULL if complete, pointer if incomplete?
  840. if (TimerRqPtr && (!CheckIO((struct IORequest *) TimerRqPtr)))
  841.                         {       AbortIO((struct IORequest *) TimerRqPtr);
  842.                                 WaitIO((struct IORequest *) TimerRqPtr);
  843.                         }
  844.                                 freefx();
  845.                                 for (i = 0; i <= SAMPLES; i++)
  846.                                     if (samp[i].base)
  847.                                         FreeMem(samp[i].base, samp[i].size);
  848. if (mode == MUSIC)        StopPlayer();
  849. if (SongPtr)            UnLoadModule(SongPtr);
  850. if (MEDPlayerBase)    {    FreePlayer();
  851.                                 CloseLibrary((struct Library *) MEDPlayerBase);
  852.                         }
  853.  
  854. if (ASLRqPtr)            FreeAslRequest(ASLRqPtr);
  855. if (OldWindowPtr)        ProcessPtr->pr_WindowPtr = OldWindowPtr;
  856. if (MainWindowPtr)    {    clearkybd();
  857.                                 ClearMenuStrip(MainWindowPtr);
  858.                                 CloseWindow(MainWindowPtr);                                             }
  859. if (GListPtr)            FreeGadgets(GListPtr);
  860. if (MenuPtr)            FreeMenus(MenuPtr);
  861. if (VisualInfoPtr)        FreeVisualInfo(VisualInfoPtr);
  862. if (ScreenPtr)            CloseScreen(ScreenPtr);
  863. if (FontPtr)            CloseFont(FontPtr);
  864. if (!TimerClosed)               CloseDevice((struct IORequest *) TimerRqPtr);
  865. if (TimerRqPtr)                 DeleteIORequest(TimerRqPtr);
  866. if (TimerPortPtr)        DeleteMsgPort(TimerPortPtr);
  867. if (PausedValPtr)        FreeMem(PausedValPtr, sizeof(struct timeval));
  868. if (CurrentValPtr)        FreeMem(CurrentValPtr, sizeof(struct timeval));
  869. if (StartValPtr)        FreeMem(StartValPtr, sizeof(struct timeval));
  870. if (DiskFontBase)        CloseLibrary((struct Library *) DiskFontBase);
  871. if (LowLevelBase)
  872. {   for (i = 0; i <= 3; i++)
  873.     {   SetJoyPortAttrs(worm[i].port, SJA_Reinitialize, TRUE, TAG_DONE);
  874.     }
  875.     CloseLibrary((struct Library *) LowLevelBase);
  876. }
  877. if (UtilityBase)                CloseLibrary((struct Library *) UtilityBase);
  878. if (ASLBase)            CloseLibrary((struct Library *) ASLBase);
  879. if (GadToolsBase)        CloseLibrary((struct Library *) GadToolsBase);
  880. if (GfxBase)            CloseLibrary((struct Library *) GfxBase);
  881. if (IconBase)            CloseLibrary((struct Library *) IconBase);
  882. if (IntuitionBase)    {    OpenWorkBench();
  883.                                 CloseLibrary((struct Library *) IntuitionBase);                                    }
  884.                                 SetTaskPri((struct Task *) ProcessPtr, OldPri);
  885. if (ArgsPtr)            FreeArgs(ArgsPtr);
  886.  
  887.     for (i = 0; i <= 3; i++)
  888.     {    ConfigBuffer[i] = (SBYTE) worm[i].control;
  889.     }
  890.     ConfigBuffer[ 4] = (UBYTE)   (DisplayID     / 16777216);
  891.     ConfigBuffer[ 5] = (UBYTE)  ((DisplayID     % 16777216) / 65536);
  892.     ConfigBuffer[ 6] = (UBYTE) (((DisplayID     % 16777216) % 65536) / 256);
  893.     ConfigBuffer[ 7] = (UBYTE) (((DisplayID     % 16777216) % 65536) % 256);
  894.     ConfigBuffer[ 8] = (UBYTE)   (DisplayWidth  / 16777216);
  895.     ConfigBuffer[ 9] = (UBYTE)  ((DisplayWidth  % 16777216) / 65536);
  896.     ConfigBuffer[10] = (UBYTE) (((DisplayWidth  % 16777216) % 65536) / 256);
  897.     ConfigBuffer[11] = (UBYTE) (((DisplayWidth  % 16777216) % 65536) % 256);
  898.     ConfigBuffer[12] = (UBYTE)   (DisplayHeight / 16777216);
  899.     ConfigBuffer[13] = (UBYTE)  ((DisplayHeight % 16777216) / 65536);
  900.     ConfigBuffer[14] = (UBYTE) (((DisplayHeight % 16777216) % 65536) / 256);
  901.     ConfigBuffer[15] = (UBYTE) (((DisplayHeight % 16777216) % 65536) % 256);
  902.     ConfigBuffer[16] = (UBYTE)   (DisplayDepth  / 65536);
  903.     ConfigBuffer[17] = (UBYTE)   (DisplayDepth  % 65536);
  904.     ConfigBuffer[18] = (UBYTE) randomflag;
  905.     ConfigBuffer[19] = (UBYTE) icons;
  906.     ConfigBuffer[20] = (UBYTE) thick;
  907.     ConfigBuffer[21] = (UBYTE) anims;
  908.  
  909.     if (FileHandle = Open("PROGDIR:WormWars.config", MODE_NEWFILE))
  910.     {   Write(FileHandle, ConfigBuffer, 22);
  911.         Close(FileHandle);
  912.         // FileHandle = NULL;
  913.     }
  914.  
  915.     exit(rc); /* End of program. */
  916. }
  917.  
  918. void clearkybd(void)
  919. {   struct IntuiMessage* MsgPtr;
  920.  
  921.     while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
  922.         GT_ReplyIMsg(MsgPtr);
  923. }
  924.  
  925. void effect(SBYTE index)
  926. {   AUTO    SBYTE i;
  927.     AUTO    SBYTE ok            = -1;
  928.     AUTO    ULONG oldestreceipt = (ULONG) -1L;
  929.     PERSIST ULONG nextreceipt   = 1L;
  930.  
  931.     /* oldestreceipt = temporary variable for ascertaining oldest
  932.                        sound still playing.
  933.     nextreceipt = next unused receipt number (monotonically incrementing). */
  934.  
  935.     if (mode == FX)
  936.     {    for (i = 0; i <= 3; i++)
  937.         {    /* decide on a channel */
  938.  
  939.             if (ok == -1)
  940.             {    if (!eversent[i])
  941.                     ok = i;
  942.                                 // CheckIO() returns NULL if complete, pointer if incomplete.
  943.                                 elif (CheckIO((struct IORequest *) AudioRqPtr[i]))
  944.                 {    WaitIO((struct IORequest *) AudioRqPtr[i]);
  945.                     ok = i;
  946.         }    }    }
  947.         if (ok == -1)
  948.         {   for (i = 0; i <= 3; i++)
  949.                 if (receipter[i] < oldestreceipt)
  950.                 {   ok = i;
  951.                     oldestreceipt = receipter[i];
  952.                 }
  953.             AbortIO((struct IORequest *) AudioRqPtr[ok]);
  954.             WaitIO((struct IORequest *) AudioRqPtr[ok]);
  955.         }
  956.         eversent[ok] = TRUE;
  957.         AudioRqPtr[ok]->ioa_Cycles              = 1;
  958.         AudioRqPtr[ok]->ioa_Request.io_Command  = CMD_WRITE;
  959.         AudioRqPtr[ok]->ioa_Request.io_Flags    = ADIOF_PERVOL;
  960.         AudioRqPtr[ok]->ioa_Request.io_Unit     = (struct Unit *) (1 << ok);
  961.         AudioRqPtr[ok]->ioa_Volume              = samp[index].volume;
  962.         AudioRqPtr[ok]->ioa_Period              = (UWORD) samp[index].speed;
  963.         AudioRqPtr[ok]->ioa_Request.io_Message.mn_ReplyPort
  964.                                                 = AudioPortPtr[ok];
  965.         AudioRqPtr[ok]->ioa_Data                = (UBYTE *) samp[index].base;
  966.         AudioRqPtr[ok]->ioa_Length              = samp[index].length[samp[index].bank];
  967.         BeginIO(AudioRqPtr[ok]);
  968.         receipter[ok] = nextreceipt;
  969. }   }
  970.  
  971. AGLOBAL void help(UBYTE type)
  972. {   TEXT  title[10], tempstring[12];
  973.     SWORD x, y;
  974.     UBYTE i;
  975.  
  976.     effect(FXHELP);
  977.     if (type == ORB)
  978.     {   strcpy(title, "Creatures");
  979.         x = 232;
  980.         y = 28 + ((CREATUREHELPS + 1) * SQUAREY);
  981.     } else
  982.     {   strcpy(title, "Objects");
  983.         x = 640;
  984.         y = 32 + ((LASTOBJECT / 2) * SQUAREY);
  985.     }
  986.     if (!(HelpWindowPtr = (struct Window *) OpenWindowTags(NULL,
  987.     WA_Left,          (SCREENXPIXEL / 2) - (x / 2),
  988.     WA_Top,           (SCREENYPIXEL / 2) - (y / 2),
  989.     WA_Width,         x,
  990.     WA_Height,        y,
  991.     WA_IDCMP,         IDCMP_CLOSEWINDOW |
  992.                       IDCMP_RAWKEY |
  993.                       IDCMP_MOUSEBUTTONS |
  994.                       IDCMP_INTUITICKS,
  995.     WA_Title,         title,
  996.     WA_Gadgets,       NULL,
  997.     WA_CustomScreen,  ScreenPtr,
  998.     WA_DragBar,       TRUE,
  999.     WA_CloseGadget,   TRUE,
  1000.     WA_NoCareRefresh, TRUE,
  1001.     WA_Activate,      TRUE,
  1002.     TAG_DONE)))
  1003.     {   say("Can't open help window!", RED);
  1004.         anykey(TRUE);
  1005.     } else
  1006.     {   SetAPen(HelpWindowPtr->RPort, WHITE);
  1007.         if (type == ORB)
  1008.         {   Move(HelpWindowPtr->RPort,  26 + (14 * FONTX), 20);
  1009.             Text(HelpWindowPtr->RPort, "Pts   Dmg", 9);
  1010.             Move(HelpWindowPtr->RPort,  26, 22);
  1011.             Draw(HelpWindowPtr->RPort, 212, 22);
  1012.             for (i = 0; i <= CREATUREHELPS; i++)
  1013.             {   Image.ImageData = ImageData[creaturehelp[i].image];
  1014.                 DrawImage
  1015.                 (   HelpWindowPtr->RPort,
  1016.                     &Image,
  1017.                     10,
  1018.                     22 + (i * SQUAREY)
  1019.                 );
  1020.                 Move(HelpWindowPtr->RPort, 26           , 31 + (i * SQUAREY));
  1021.                 Text(HelpWindowPtr->RPort, creaturehelp[i].desc, strlen(creaturehelp[i].desc));
  1022.  
  1023.                 stcl_d(tempstring, creaturehelp[i].score);
  1024.                 align(tempstring, 3, ' ');
  1025.                 Move(HelpWindowPtr->RPort, 26 + (14 * 8), 31 + (i * SQUAREY));
  1026.                 Text(HelpWindowPtr->RPort, tempstring, 3);
  1027.  
  1028.                 stcl_d(tempstring, creaturehelp[i].pain);
  1029.                 Move(HelpWindowPtr->RPort, 26 + (21 * 8), 31 + (i * SQUAREY));
  1030.                 Text(HelpWindowPtr->RPort, tempstring, strlen(tempstring));
  1031.         }   }
  1032.         else
  1033.         {   // assert(type == AFFIXER);
  1034.             for (i = 0; i <= LASTOBJECT; i++)
  1035.             {   Image.ImageData = ImageData[i];
  1036.                 if (i <= LASTOBJECT / 2)
  1037.                 {   Move(HelpWindowPtr->RPort, 26, 24 + (i * SQUAREY));
  1038.                     DrawImage
  1039.                     (   HelpWindowPtr->RPort,
  1040.                         &Image,
  1041.                         10,
  1042.                         15 + (i * SQUAREY)
  1043.                     );
  1044.                 } else
  1045.                 {   Move(HelpWindowPtr->RPort, 354, 24 - SQUAREY + ((i - (LASTOBJECT / 2)) * SQUAREY));
  1046.                     DrawImage
  1047.                     (   HelpWindowPtr->RPort,
  1048.                         &Image,
  1049.                         338,
  1050.                         15 - SQUAREY + ((i - (LASTOBJECT / 2)) * SQUAREY)
  1051.                     );
  1052.                 }
  1053.                 Text(HelpWindowPtr->RPort, objectdesc[i], strlen(objectdesc[i]));
  1054.         }   }
  1055.         helploop(type);
  1056. }   }
  1057.  
  1058. AGLOBAL void helpabout(void)
  1059. {       SBYTE line;
  1060.         SLONG projectval;
  1061.         TEXT  projectstring[7], ks[5] = "    ", wb[5] = "    ";
  1062.         ULONG ksval = SysBase->LibNode.lib_Version,
  1063.               wbval = IconBase->lib_Version;
  1064.  
  1065.         effect(FXHELP);
  1066.     if (!(HelpWindowPtr = (struct Window *) OpenWindowTags(NULL,
  1067.     WA_Left,            (SCREENXPIXEL / 2) - (ABOUTXPIXEL / 2),
  1068.     WA_Top,                (SCREENYPIXEL / 2) - (ABOUTYPIXEL / 2),
  1069.     WA_Width,            ABOUTXPIXEL,
  1070.     WA_Height,            ABOUTYPIXEL,
  1071.     WA_IDCMP,            IDCMP_CLOSEWINDOW | IDCMP_RAWKEY,
  1072.     WA_Title,            "About Worm Wars",
  1073.     WA_Gadgets,            NULL,
  1074.         WA_CustomScreen,  ScreenPtr,
  1075.         WA_DragBar,       TRUE,
  1076.         WA_CloseGadget,   TRUE,
  1077.         WA_NoCareRefresh, TRUE,
  1078.         WA_Activate,      TRUE,
  1079.     TAG_DONE)))
  1080.         {   say("Can't open About... window!", RED);
  1081.             anykey(TRUE);
  1082.     } else
  1083.     {    /* calculate project size */
  1084.         
  1085.         projectval =
  1086.                   10                                 // header
  1087.                 + ((HISCORES + 1) * HISCORESIZE)     // high scores
  1088.                 + ((levels + 1) * (8 + (LEVELSIZE))) // level data
  1089.                 + strlen(VERSION);                   // version string
  1090.  
  1091.                 switch(ksval)
  1092.                 {
  1093.                 case 37:
  1094.                     strcpy(ks, "2.04");
  1095.                 break;
  1096.                 case 38:
  1097.                     strcpy(ks, "2.1 ");
  1098.                 break;
  1099.                 case 39:
  1100.                     strcpy(ks, "3.0 ");
  1101.                 break;
  1102.                 case 40:
  1103.                 case 41:
  1104.                 case 42:
  1105.                 case 43:
  1106.                     strcpy(ks, "3.1 ");
  1107.                 break;
  1108.                 case 44:
  1109.                     strcpy(ks, "3.5 ");
  1110.                 break;
  1111.                 case 45:
  1112.                     strcpy(ks, "3.9 ");
  1113.                 break;
  1114.                 default:
  1115.                     strcpy(ks, "4.0+");
  1116.                 break;
  1117.                 }
  1118.  
  1119.                 switch(wbval)
  1120.                 {
  1121.                 case 37:
  1122.                     strcpy(wb, "2.04");
  1123.                 break;
  1124.                 case 38:
  1125.                     strcpy(wb, "2.1 ");
  1126.                 break;
  1127.                 case 39:
  1128.                     strcpy(wb, "3.0 ");
  1129.                 break;
  1130.                 case 40:
  1131.                 case 41:
  1132.                 case 42:
  1133.                 case 43:
  1134.                     strcpy(wb, "3.1 ");
  1135.                 break;
  1136.                 case 44:
  1137.                     strcpy(wb, "3.5 ");
  1138.                 break;
  1139.                 case 45:
  1140.                     strcpy(wb, "3.9 ");
  1141.                 break;
  1142.                 default:
  1143.                     strcpy(wb, "4.0+");
  1144.                 break;
  1145.                 }
  1146.                 
  1147.         SetAPen(HelpWindowPtr->RPort, worm[rand() % 4].colour);
  1148.         RectFill(HelpWindowPtr->RPort, 8, 13, ABOUTXPIXEL - 11, ABOUTYPIXEL - 6);
  1149.         SetAPen(HelpWindowPtr->RPort, ABOUTSHADOW);
  1150.         Move(HelpWindowPtr->RPort, 7, ABOUTYPIXEL - 5);
  1151.         Draw(HelpWindowPtr->RPort, 7, 12);
  1152.         Draw(HelpWindowPtr->RPort, ABOUTXPIXEL - 10, 12);
  1153.         SetAPen(HelpWindowPtr->RPort, ABOUTSHINE);
  1154.         Draw(HelpWindowPtr->RPort, ABOUTXPIXEL - 10, ABOUTYPIXEL - 5);
  1155.         Draw(HelpWindowPtr->RPort, 8, ABOUTYPIXEL - 5);
  1156.         SetAPen(HelpWindowPtr->RPort, BLACK);
  1157.         SetDrMd(HelpWindowPtr->RPort, JAM1);
  1158.         for (line = 0; line <= ABOUTLINES; line++)
  1159.                 {   Move(HelpWindowPtr->RPort, about[line].x, about[line].y);
  1160.                     Text(HelpWindowPtr->RPort, about[line].text, (SBYTE) strlen(about[line].text));
  1161.         }
  1162.         stcl_d(projectstring, projectval);
  1163.                 align(projectstring, 6, ' ');
  1164.         Move(HelpWindowPtr->RPort, PROJECTX, PROJECTY);
  1165.                 Text(HelpWindowPtr->RPort, projectstring, 6);
  1166.                 Move(HelpWindowPtr->RPort, KICKSTARTX, KICKSTARTY);
  1167.                 Text(HelpWindowPtr->RPort, ks, 4);
  1168.                 Move(HelpWindowPtr->RPort, WORKBENCHX, WORKBENCHY);
  1169.                 Text(HelpWindowPtr->RPort, wb, 4);
  1170.  
  1171.                 DrawBevelBox(HelpWindowPtr->RPort, 16, 20, 44 + 4, 38 + 4, GT_VisualInfo, VisualInfoPtr, GTBB_Recessed, TRUE, TAG_END);
  1172.                 DrawImage(HelpWindowPtr->RPort, &About, 16 + 2, 20 + 2);
  1173.  
  1174.         helploop(NOSQUARE);
  1175. }   }
  1176.  
  1177. AGLOBAL void helploop(UBYTE type)
  1178. {   ABOOL                done = FALSE;
  1179.     UBYTE                objx, objy;
  1180.     UWORD                code, qual;
  1181.     SWORD                mousex, mousey;
  1182.     SBYTE                birdframe = -1, birddir = 1;
  1183.     ULONG                class;
  1184.     struct IntuiMessage* MsgPtr;
  1185.  
  1186.     while(!done)
  1187.     {   Wait(1L << HelpWindowPtr->UserPort->mp_SigBit);
  1188.         while (MsgPtr = (struct IntuiMessage *) GetMsg(HelpWindowPtr->UserPort))
  1189.         {   class  = MsgPtr->Class;
  1190.             code   = MsgPtr->Code;
  1191.             qual   = MsgPtr->Qualifier;
  1192.             mousex = MsgPtr->MouseX;
  1193.             mousey = MsgPtr->MouseY;
  1194.             ReplyMsg((struct Message *) MsgPtr);
  1195.             switch(class)
  1196.             {
  1197.             case IDCMP_INTUITICKS:
  1198.                 if (type == ORB && anims)
  1199.                 {       birdframe += birddir;
  1200.                         Image.ImageData = ImageData[BIRD + birdframe];
  1201.                         DrawImage
  1202.                         (   HelpWindowPtr->RPort,
  1203.                             &Image,
  1204.                             10,
  1205.                             22
  1206.                         );
  1207.                         if (birdframe == 0)
  1208.                         {   birddir = 1;
  1209.                         } elif (birdframe == 4)
  1210.                         {   birddir = -1;
  1211.                 }       }
  1212.             break;
  1213.             case IDCMP_CLOSEWINDOW:
  1214.                 done = TRUE;
  1215.             break;
  1216.             case IDCMP_RAWKEY:
  1217.                 if (code == SPACEBAR || code == RETURN || code == ENTER || code == HELP)
  1218.                     done = TRUE;
  1219.                 elif (code == ESCAPE)
  1220.                 {   if (qual & IEQUALIFIER_LSHIFT || qual & IEQUALIFIER_RSHIFT)
  1221.                     {   if (verify())
  1222.                         {   CloseWindow(HelpWindowPtr);
  1223.                             cleanexit(EXIT_SUCCESS);
  1224.                    }   }
  1225.                    else done = TRUE;
  1226.                 }
  1227.             break;
  1228.             case IDCMP_MOUSEBUTTONS:
  1229.                 if (a == FIELDEDIT && type == AFFIXER)
  1230.                 {   objx = (mousex - 10) / 328; // which column
  1231.                     objy = (mousey - 13) / 10;  // which row
  1232.                     if ((objx == 0 || objx == 1) && (objy >= 0 && objy <= LASTOBJECT / 2))
  1233.                     {   if (objx)
  1234.                             setbrush(objy + 1 + (LASTOBJECT / 2));
  1235.                         else setbrush(objy);
  1236.                         done = TRUE;
  1237.                 }   }
  1238.             break;
  1239.             default:
  1240.             break;
  1241.     }   }   }
  1242.     CloseWindow(HelpWindowPtr);
  1243.     HelpWindowPtr = NULL;
  1244.     clearkybd();
  1245. }
  1246.  
  1247. AGLOBAL void filedelete(void)
  1248. {   TEXT newpathname[255], temp1[81];
  1249.  
  1250.     if (AslRequestTags(ASLRqPtr, ASL_Hail, "Delete Fieldset", ASL_FuncFlags, FILF_PATGAD | FILF_SAVE, TAG_DONE) && *(ASLRqPtr->rf_File) != 0)
  1251.     {   strcpy(newpathname, ASLRqPtr->rf_Dir);
  1252.         AddPart(newpathname, ASLRqPtr->rf_File, 254);
  1253.         if (DeleteFile(newpathname))
  1254.         {   strcpy(temp1, "Deleted ");
  1255.             strcat(temp1, newpathname);
  1256.             strcat(temp1, ".");
  1257.         } else
  1258.         {   strcpy(temp1, "Couldn't delete ");
  1259.             strcat(temp1, newpathname);
  1260.             strcat(temp1, "!");
  1261.         }
  1262.         say(temp1, WHITE);
  1263. }   }
  1264.  
  1265. AGLOBAL void fileopen(ABOOL revert)
  1266. {   TEXT temp1[81] = {"Opened "}, temp2[3], newpathname[255];
  1267.  
  1268.     strcpy(newpathname, pathname);
  1269.     if (revert || (AslRequestTags(ASLRqPtr, ASL_Hail, "Open Fieldset", ASL_FuncFlags, FILF_PATGAD, TAG_DONE) && *(ASLRqPtr->rf_File)))
  1270.     {   if (!revert)
  1271.         {   strcpy(newpathname, ASLRqPtr->rf_Dir);
  1272.             AddPart(newpathname, ASLRqPtr->rf_File, 254);
  1273.         }
  1274.         if (!loadfields(newpathname))
  1275.         {   strcpy(pathname, newpathname);
  1276.             strcat(temp1, pathname);
  1277.             strcat(temp1, " (");
  1278.             stci_d(temp2, levels);
  1279.             strcat(temp1, temp2);
  1280.             strcat(temp1, " levels).");
  1281.             say(temp1, WHITE);
  1282.             if (a == FIELDEDIT)
  1283.                 turborender();
  1284.             else hiscores();
  1285.         } else
  1286.         {   strcpy(temp1, "Couldn't open ");
  1287.             strcat(temp1, newpathname);
  1288.             strcat(temp1, "!");
  1289.             say(temp1, WHITE);
  1290.     }   }
  1291.     if (a == GAMEOVER)
  1292.         anykey(TRUE);
  1293. }
  1294.  
  1295. AGLOBAL void filesaveas(ABOOL flag)
  1296. {    ABOOL    cont = TRUE;
  1297.     TEXT    newpathname[255], temp1[SAYLIMIT + 1], temp2[3];
  1298.         struct DiskObject* InfoHandle;
  1299.  
  1300.     /* flag is TRUE for 'save as...', FALSE for 'save'. */
  1301.  
  1302.     strcpy(newpathname, pathname);
  1303.     if (flag)
  1304.         if (AslRequestTags(ASLRqPtr, ASL_Hail, "Save Fieldset", ASL_FuncFlags, FILF_PATGAD | FILF_SAVE, TAG_DONE) && *(ASLRqPtr->rf_File) != 0)
  1305.         {   strcpy(newpathname, ASLRqPtr->rf_Dir);
  1306.             AddPart(newpathname, ASLRqPtr->rf_File, 254);
  1307.         } else cont = FALSE;
  1308.     if (cont)
  1309.     {   matchteleports();
  1310.         strcpy(temp1, "Saving ");
  1311.     strcat(temp1, newpathname);
  1312.     strcat(temp1, "...");
  1313.     say(temp1, WHITE);
  1314.     if (savefields(newpathname))
  1315.         {   strcpy(pathname, newpathname);
  1316.             if (icons)
  1317.             {   InfoHandle = GetDiskObjectNew(DEFAULTSET);
  1318.                 InfoHandle->do_CurrentX = NO_ICON_POSITION;
  1319.                 InfoHandle->do_CurrentY = NO_ICON_POSITION;
  1320.                 if (!PutDiskObject(pathname, InfoHandle))
  1321.                 {   say("Couldn't write .info file!", RED);
  1322.                     anykey(TRUE);
  1323.             }   }
  1324.             strcpy(temp1, "Saved ");
  1325.             strcat(temp1, pathname);
  1326.             strcat(temp1, " (");
  1327.             stci_d(temp2, levels);
  1328.             strcat(temp1, temp2);
  1329.             strcat(temp1, " levels).");
  1330.         } else
  1331.         {   strcpy(temp1, "Couldn't save ");
  1332.             strcat(temp1, newpathname);
  1333.             strcat(temp1, "!");
  1334.         }
  1335.         say(temp1, WHITE);
  1336.         if (a == GAMEOVER)
  1337.             anykey(TRUE);
  1338. }   }
  1339.  
  1340. MODULE void freefx(void)
  1341. {   SBYTE i;
  1342.  
  1343.     stopfx();
  1344.     if (!AudioClosed)
  1345.     {   CloseDevice((struct IORequest *) AudioRqPtr[0]);
  1346.         AudioClosed = TRUE;
  1347.     }
  1348.     for (i = 0; i <= 3; i++)
  1349.     {   if (AudioRqPtr[i])
  1350.         {   DeleteIORequest(AudioRqPtr[i]);
  1351.             AudioRqPtr[i] = NULL;
  1352.         }
  1353.         if (AudioPortPtr[i])
  1354.         {   // if we ReplyMsg() first, that causes crashes
  1355.             DeleteMsgPort(AudioPortPtr[i]);
  1356.             AudioPortPtr[i] = NULL;
  1357.     }   }
  1358.     if (fbase)
  1359.     {   FreeMem(fbase, fsize);
  1360.         fbase = NULL;
  1361.     }
  1362.     if (FilePtr)
  1363.     {   Close(FilePtr);
  1364.         FilePtr = NULL;
  1365. }   }
  1366.  
  1367. void gameinput(void)
  1368. {   ABOOL                allowed = TRUE,
  1369.                          done;
  1370.     UWORD                code, qual;
  1371.     ULONG                class;
  1372.     struct IntuiMessage* MsgPtr;
  1373.     SBYTE                keyplayer, i;
  1374.     UBYTE                which;
  1375.  
  1376.     for (which = 0; which <= NUMKEYS; which++)
  1377.         key[which].down = FALSE;
  1378.  
  1379. /* keyboard */
  1380.  
  1381. while (MsgPtr = (struct IntuiMessage *) GetMsg(MainWindowPtr->UserPort))
  1382. {   class = MsgPtr->Class;
  1383.     code  = MsgPtr->Code;
  1384.     qual  = MsgPtr->Qualifier;
  1385.     ReplyMsg((struct Message *) MsgPtr);
  1386.     if (class == IDCMP_RAWKEY && (!(qual & IEQUALIFIER_REPEAT)))
  1387.     {   if (code < KEYUP)
  1388.         {   switch(code)
  1389.             {
  1390.             case M:
  1391.                 toggle(M);
  1392.             break;
  1393.                 case F:
  1394.                 toggle(F);
  1395.             break;
  1396.             case KEY_T:
  1397.                 // check whether any human worms are playing
  1398.                 for (i = 0; i <= 3; i++)
  1399.                 {   if
  1400.                     (   worm[i].control != AMIGA
  1401.                      && worm[i].control != NONE
  1402.                      && worm[i].lives
  1403.                     )
  1404.                     {   allowed = FALSE;
  1405.                         break;
  1406.                 }   }
  1407.                 if (allowed)
  1408.                 {   if (turbo)
  1409.                     {   turbo = FALSE;
  1410.                     } else
  1411.                     {   turbo = TRUE;
  1412.                         draw(CLOCKICON, ICONY, CLOCK);
  1413.                 }   }
  1414.             break;
  1415.             case P:
  1416.                 clearkybd();
  1417.                 say("Paused...press P to unpause", WHITE);
  1418.                 pausetimer();
  1419.                 done = FALSE;
  1420.                 while (!done)
  1421.                 {   Wait(1L << MainWindowPtr->UserPort->mp_SigBit);
  1422.                     while (MsgPtr = (struct IntuiMessage *) GetMsg(MainWindowPtr->UserPort))
  1423.                     {   class = MsgPtr->Class;
  1424.                         code  = MsgPtr->Code;
  1425.                         qual  = MsgPtr->Qualifier;
  1426.                         ReplyMsg((struct Message *) MsgPtr);
  1427.                         if (class == IDCMP_RAWKEY && (!(qual & IEQUALIFIER_REPEAT)))
  1428.                         {   if (code == ESCAPE)
  1429.                             {   if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  1430.                                 {   if (verify())
  1431.                                         cleanexit(EXIT_SUCCESS);
  1432.                                 } else
  1433.                                 {   a = GAMEOVER;
  1434.                                     done = TRUE;
  1435.                                     worm[0].lives = worm[1].lives = worm[2].lives = worm[3].lives = 0;
  1436.                             }   }
  1437.                             elif (code == M)
  1438.                                 toggle(M);
  1439.                             elif (code == F)
  1440.                                 toggle(F);
  1441.                             elif (code == P)
  1442.                             {   say("Unpaused", WHITE);
  1443.                                 done = TRUE;
  1444.                 }   }   }   }
  1445.                 unpausetimer();
  1446.             break;
  1447.             case ESCAPE:
  1448.                 if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  1449.                 {   if (verify())
  1450.                         cleanexit(EXIT_SUCCESS);
  1451.                 } else
  1452.                 {   a = GAMEOVER;
  1453.                     worm[0].lives = worm[1].lives = worm[2].lives = worm[3].lives = 0;
  1454.                 }
  1455.             break;
  1456.             default:
  1457.                for (which = 0; which <= NUMKEYS; which++)
  1458.                    if (code == key[which].scancode)
  1459.                        key[which].down = TRUE;
  1460.             break;
  1461.     }   }   }
  1462.     elif (class == IDCMP_CLOSEWINDOW)
  1463.         cleanexit(EXIT_SUCCESS);
  1464.     elif (class == IDCMP_REFRESHWINDOW)
  1465.     {   GT_BeginRefresh(MainWindowPtr);
  1466.         GT_EndRefresh(MainWindowPtr, TRUE);
  1467. }   }
  1468.  
  1469. for (which = 0; which <= NUMKEYS; which++)
  1470. {   if (key[which].down)
  1471.     {   if (key[which].special == ONEHUMAN)
  1472.         {   if
  1473.             (   (worm[0].control == KEYBOARD && worm[1].control != KEYBOARD)
  1474.             )
  1475.                 wormqueue(0, key[which].deltax, key[which].deltay);
  1476.             elif
  1477.             (   (worm[1].control == KEYBOARD && worm[0].control != KEYBOARD)
  1478.             )
  1479.                 wormqueue(1, key[which].deltax, key[which].deltay);
  1480.         } elif (key[which].special == MOVE || key[which].special == AMMO)
  1481.         {   if (worm[key[which].player].control == KEYBOARD)
  1482.                 keyplayer = key[which].player;
  1483.             elif
  1484.             (   key[which].player == 1
  1485.              && (worm[0].control == KEYBOARD && worm[1].control != KEYBOARD)
  1486.             )
  1487.                 keyplayer = 0;
  1488.             elif
  1489.             (   key[which].player == 0
  1490.              && (worm[1].control == KEYBOARD && worm[0].control != KEYBOARD)
  1491.             )
  1492.                 keyplayer = 1;
  1493.             else keyplayer = -1;
  1494.             if (keyplayer != -1)
  1495.             {   wormqueue(keyplayer, key[which].deltax, key[which].deltay);
  1496.         }   }
  1497.         elif (worm[1].lives) /* assumes key[which].special == TRAINER */
  1498.             train(key[which].scancode);
  1499. }   }
  1500. }
  1501.  
  1502. void hiscores(void)
  1503. {   SBYTE which;
  1504.     TEXT  tempstring[NAMELENGTH + 1];
  1505.  
  1506.     /* render hiscores
  1507.  
  1508.     #################################################### # = shadow
  1509.     #   #   #   #                      #     #         % % = shine
  1510.     #   #   #   #                      #     #         %
  1511.     #   #   #   #                      #     #         %
  1512.     #   #   #   #                      #     #         %
  1513.     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */
  1514.  
  1515.     hiframe = 0;
  1516.     SetDrMd(MainWindowPtr->RPort, JAM1);
  1517.     for (which = 0; which <= HISCORES; which++)
  1518.     {   if (hiscore[which].player == -1)
  1519.             SetAPen(MainWindowPtr->RPort, LIGHTGREY);
  1520.         else SetAPen(MainWindowPtr->RPort, worm[hiscore[which].player].colour);
  1521.         RectFill(MainWindowPtr->RPort, 104, TSOFFSET + 4 + (which * HISCOREDISTANCE), 536, TSOFFSET + 13 + (which * HISCOREDISTANCE));
  1522.     if (hiscore[which].player == 0)
  1523.             SetAPen(MainWindowPtr->RPort, DARKGREEN);
  1524.         elif (hiscore[which].player == 1)
  1525.             SetAPen(MainWindowPtr->RPort, DARKRED);
  1526.         elif (hiscore[which].player == 2)
  1527.             SetAPen(MainWindowPtr->RPort, DARKBLUE);
  1528.         elif (hiscore[which].player == 3)
  1529.             SetAPen(MainWindowPtr->RPort, DARKYELLOW);
  1530.     else SetAPen(MainWindowPtr->RPort, DARKGREY);
  1531.         Move(MainWindowPtr->RPort, 103, TSOFFSET + 14 + (which * HISCOREDISTANCE));
  1532.         Draw(MainWindowPtr->RPort, 103, TSOFFSET +  3 + (which * HISCOREDISTANCE));
  1533.         Draw(MainWindowPtr->RPort, 537, TSOFFSET +  3 + (which * HISCOREDISTANCE));
  1534.  
  1535.     if (hiscore[which].player != -1)
  1536.         {   /* divider bars */
  1537.     
  1538.             Move(MainWindowPtr->RPort, 182 - 55, TSOFFSET +  3 + (which * HISCOREDISTANCE));
  1539.             Draw(MainWindowPtr->RPort, 182 - 55, TSOFFSET + 13 + (which * HISCOREDISTANCE));
  1540.             Move(MainWindowPtr->RPort, 254 - 55, TSOFFSET +  3 + (which * HISCOREDISTANCE));
  1541.             Draw(MainWindowPtr->RPort, 254 - 55, TSOFFSET + 13 + (which * HISCOREDISTANCE));
  1542.             Move(MainWindowPtr->RPort, 290 - 55, TSOFFSET +  3 + (which * HISCOREDISTANCE));
  1543.             Draw(MainWindowPtr->RPort, 290 - 55, TSOFFSET + 13 + (which * HISCOREDISTANCE));
  1544.             Move(MainWindowPtr->RPort, 416,      TSOFFSET +  3 + (which * HISCOREDISTANCE));
  1545.             Draw(MainWindowPtr->RPort, 416,      TSOFFSET + 13 + (which * HISCOREDISTANCE));
  1546.             Move(MainWindowPtr->RPort, 464,      TSOFFSET +  3 + (which * HISCOREDISTANCE));
  1547.             Draw(MainWindowPtr->RPort, 464,      TSOFFSET + 13 + (which * HISCOREDISTANCE));
  1548.     }
  1549.     
  1550.     SetAPen(MainWindowPtr->RPort, WHITE);
  1551.         Move(MainWindowPtr->RPort, 159 - 55, TSOFFSET + 14 + (which * HISCOREDISTANCE));
  1552.         Draw(MainWindowPtr->RPort, 482 + 55, TSOFFSET + 14 + (which * HISCOREDISTANCE));
  1553.         Draw(MainWindowPtr->RPort, 482 + 55, TSOFFSET +  4 + (which * HISCOREDISTANCE));
  1554.     SetAPen(MainWindowPtr->RPort, BLACK);
  1555.  
  1556.     if (hiscore[which].player != -1)
  1557.         {   stci_d(tempstring, which + 1);
  1558.             tempstring[1] = '.';
  1559.             Move(MainWindowPtr->RPort, 161 - 55, TSOFFSET + 11 + (which * HISCOREDISTANCE));
  1560.             Text(MainWindowPtr->RPort, tempstring, 2);
  1561.             stci_d(tempstring, hiscore[which].score);
  1562.             align(tempstring, 7, ' ');
  1563.             Move(MainWindowPtr->RPort, 193 - 55, TSOFFSET + 11 + (which * HISCOREDISTANCE));
  1564.             Text(MainWindowPtr->RPort, tempstring, 7);
  1565.             if (hiscore[which].level == -1)
  1566.             {   strcpy(tempstring, "All");
  1567.             } else
  1568.             {   stci_d(tempstring, hiscore[which].level);
  1569.                 align(tempstring, 3, ' ');
  1570.             }
  1571.             Move(MainWindowPtr->RPort, 206, TSOFFSET + 11 + (which * HISCOREDISTANCE));
  1572.             Text(MainWindowPtr->RPort, tempstring, 3);
  1573.             Move(MainWindowPtr->RPort, 241, TSOFFSET + 11 + (which * HISCOREDISTANCE));
  1574.             Text(MainWindowPtr->RPort, hiscore[which].name, strlen(hiscore[which].name));
  1575.             Move(MainWindowPtr->RPort, 418, TSOFFSET + 11 + (which * HISCOREDISTANCE));
  1576.             Text(MainWindowPtr->RPort, hiscore[which].time, strlen(hiscore[which].time));
  1577.             Move(MainWindowPtr->RPort, 466, TSOFFSET + 11 + (which * HISCOREDISTANCE));
  1578.             Text(MainWindowPtr->RPort, hiscore[which].date, strlen(hiscore[which].date));
  1579.  
  1580.             Image.ImageData = ImageData[missileframes[hiscore[which].player][0]];
  1581.             DrawImage(MainWindowPtr->RPort, &Image, 540, TSOFFSET + 3 + (which * HISCOREDISTANCE));
  1582.     }   }
  1583.     SetDrMd(MainWindowPtr->RPort, JAM2);
  1584. }
  1585.  
  1586. void hiscorenames(void)
  1587. {   ULONG                class;
  1588.     ABOOL                done;
  1589.     SBYTE                which;
  1590.     struct IntuiMessage* MsgPtr;
  1591.  
  1592.     for (which = 0; which <= HISCORES; which++)
  1593.     {   if (hiscore[which].fresh)
  1594.         {   GT_SetGadgetAttrs(StringGadgetPtr[which], MainWindowPtr, NULL, GA_Disabled, FALSE, GTST_String, worm[hiscore[which].player].name, TAG_DONE);
  1595.             ActivateGadget(StringGadgetPtr[which], MainWindowPtr, NULL);
  1596.             done = FALSE;
  1597.             while (!done)
  1598.             {   while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
  1599.                 {   class = MsgPtr->Class;
  1600.                     GT_ReplyIMsg(MsgPtr);
  1601.                     if (class == IDCMP_GADGETUP)
  1602.                     {   done = TRUE;
  1603.                     } elif (class == IDCMP_MOUSEBUTTONS)
  1604.                     {   ActivateGadget(StringGadgetPtr[which], MainWindowPtr, NULL);
  1605.                     } elif (class == IDCMP_REFRESHWINDOW)
  1606.                     {   GT_BeginRefresh(MainWindowPtr);
  1607.                         GT_EndRefresh(MainWindowPtr, TRUE);
  1608.             }   }   }
  1609.             GT_SetGadgetAttrs(StringGadgetPtr[which], MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
  1610.             effect(FXAPPLAUSE);
  1611.             strcpy(hiscore[which].name, ((struct StringInfo *) (StringGadgetPtr[which]->SpecialInfo))->Buffer);
  1612.             if (hiscore[which].name[0] >= 'a' && hiscore[which].name[0] <= 'z')
  1613.             {   hiscore[which].name[0] -= 32;
  1614.             }
  1615.             strcpy(worm[hiscore[which].player].name, hiscore[which].name);
  1616.             hiscore[which].fresh = FALSE;
  1617.             hiscores();
  1618. }   }   }
  1619.  
  1620. MODULE ABOOL beginfx(void)
  1621. {           SBYTE    i;
  1622.     PERSIST    UBYTE    chan[]    = {15};
  1623.  
  1624.     for (i = 0; i <= 3; i++)
  1625.     {   eversent[i] = FALSE;
  1626.         if (!(AudioPortPtr[i] = (struct MsgPort *) CreateMsgPort()))
  1627.         {   freefx();
  1628.             draw(MUSICICON, ICONY, BLACKENED);
  1629.             mode = FALSE;
  1630.             say("No port for effects!", RED);
  1631.             anykey(TRUE);
  1632.             return FALSE;
  1633.         } else if (!(AudioRqPtr[i] = (struct IOAudio *) CreateIORequest(AudioPortPtr[i], sizeof(struct IOAudio))))
  1634.         {   freefx();
  1635.             draw(MUSICICON, ICONY, BLACKENED);
  1636.             mode = FALSE;
  1637.             say("No I/O memory for effects!", RED);
  1638.             anykey(TRUE);
  1639.             return FALSE;
  1640.     }   }
  1641.     AudioRqPtr[0]->ioa_Request.io_Message.mn_ReplyPort      = AudioPortPtr[0];
  1642.     AudioRqPtr[0]->ioa_Request.io_Message.mn_Node.ln_Pri    = 127;
  1643.     AudioRqPtr[0]->ioa_AllocKey                             = 0;
  1644.     AudioRqPtr[0]->ioa_Data                                 = chan;
  1645.     AudioRqPtr[0]->ioa_Length                               = 1;
  1646.     if (AudioClosed = OpenDevice(AUDIONAME, 0L, (struct IORequest *) AudioRqPtr[0], 0L))
  1647.     {   freefx();
  1648.         draw(MUSICICON, ICONY, BLACKENED);
  1649.         mode = FALSE;
  1650.         say("Can't allocate all channels for effects!", RED);
  1651.         anykey(TRUE);
  1652.         return FALSE;
  1653.     } else
  1654.     {   for (i = 1; i <= 3; i++)
  1655.             CopyMem(AudioRqPtr[0], AudioRqPtr[i], sizeof(struct IOAudio));
  1656.         return TRUE;
  1657. }   }
  1658.  
  1659. void resettime(void)
  1660. {   GetSysTime(StartValPtr);
  1661.     srand((UWORD) StartValPtr->tv_micro);
  1662. }
  1663.  
  1664. void rundown(SBYTE player)
  1665. {       ABOOL                done = FALSE, flag = FALSE;
  1666.         TEXT                 tempstring[6];
  1667.     SBYTE                i, j, x, y;
  1668.         UBYTE                multiply = worm[player].multi * players;
  1669.     UWORD                counter = 0,
  1670.                              code,
  1671.                              qual;
  1672.         ULONG                levelbonus,
  1673.                              numberbonus,
  1674.                              timebonus,
  1675.                              tailbonus,
  1676.                              class;
  1677.     struct IntuiMessage* MsgPtr;
  1678.  
  1679.         clearscreen();                                                                           
  1680.         DrawBevelBox(MainWindowPtr->RPort, CENTREXPIXEL - 150, CENTREYPIXEL - 40, 308, 59, GT_VisualInfo, VisualInfoPtr, TAG_END);
  1681.         SetAPen(MainWindowPtr->RPort, BLACK);
  1682.         RectFill
  1683.         (   MainWindowPtr->RPort,
  1684.             CENTREXPIXEL - 149,
  1685.             CENTREYPIXEL -  39,
  1686.             CENTREXPIXEL + 148,
  1687.             CENTREYPIXEL +  17
  1688.         );
  1689.  
  1690.         SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1691.                                         
  1692.         draw(CENTREX - 11, CENTREY - 2, TREASURE);
  1693.         if (worm[player].numbers == 0)
  1694.         {   draw(CENTREX - 11, CENTREY - 1, FIRSTPAIN + player);
  1695.         } else
  1696.         {   draw(CENTREX - 11, CENTREY - 1, FIRSTLETTER + worm[player].numbers - 1);
  1697.         }
  1698.         draw(CENTREX - 11, CENTREY    , CLOCK);
  1699.         draw(CENTREX - 11, CENTREY + 1, FIRSTGLOW + player);
  1700.  
  1701.     Move(MainWindowPtr->RPort, RUNDOWNX,                RUNDOWNY + RUNDOWNY_2ND                );
  1702.     Text(MainWindowPtr->RPort, " Level Bonus:   ## x #### =", RUNDOWNLENGTH);
  1703.                              // 012345678901234567890123456
  1704.     // now the first quantity
  1705.     stci_d(tempstring, level - 1);
  1706.     align(tempstring, 4, ' ');
  1707.     Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_2ND, RUNDOWNY + RUNDOWNY_2ND                );
  1708.     Text(MainWindowPtr->RPort, tempstring, 4);
  1709.     // now the second quantity
  1710.     stci_d(tempstring, BONUS_LEVEL * multiply);
  1711.     align(tempstring, 4, ' ');
  1712.     Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_3RD, RUNDOWNY + RUNDOWNY_2ND                );
  1713.     Text(MainWindowPtr->RPort, tempstring, 4);
  1714.  
  1715.     Move(MainWindowPtr->RPort, RUNDOWNX,                RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 1));
  1716.     Text(MainWindowPtr->RPort, "Number Bonus:   ## x #### =", RUNDOWNLENGTH);
  1717.     // now the first quantity
  1718.     stci_d(tempstring, worm[player].numbers);
  1719.     align(tempstring, 4, ' ');
  1720.     Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_2ND, RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 1));
  1721.     Text(MainWindowPtr->RPort, tempstring, 4);
  1722.     // now the second quantity
  1723.     stci_d(tempstring, BONUS_NUMBER * multiply);
  1724.     align(tempstring, 4, ' ');
  1725.     Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_3RD, RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 1));
  1726.     Text(MainWindowPtr->RPort, tempstring, 4);
  1727.  
  1728.     Move(MainWindowPtr->RPort, RUNDOWNX,                RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 2));
  1729.     Text(MainWindowPtr->RPort, "  Time Bonus: #:## x #### =", RUNDOWNLENGTH);
  1730.     // now the first quantity
  1731.     tempstring[0] = 48 +  (secondsleft / 60);
  1732.     tempstring[1] = ':';
  1733.     tempstring[2] = 48 + ((secondsleft % 60) / 10);
  1734.     tempstring[3] = 48 + ((secondsleft % 60) % 10);
  1735.     Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_2ND, RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 2));
  1736.     Text(MainWindowPtr->RPort, tempstring, 4);
  1737.     // now the second quantity
  1738.     stci_d(tempstring, BONUS_TIME * multiply);
  1739.     align(tempstring, 4, ' ');                                        
  1740.     Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_3RD, RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 2));
  1741.     Text(MainWindowPtr->RPort, tempstring, 4);
  1742.  
  1743.     Move(MainWindowPtr->RPort, RUNDOWNX,                RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 3));
  1744.     Text(MainWindowPtr->RPort, "  Tail Bonus:   ## x #### =", RUNDOWNLENGTH);
  1745.     // now the first quantity
  1746.     for (x = 0; x <= FIELDX; x++)
  1747.     {   for (y = 0; y <= FIELDY; y++)
  1748.         {   if (field[x][y] == FIRSTTAIL + player || field[x][y] == FIRSTGLOW + player)
  1749.             {   counter++;
  1750.     }   }   }
  1751.     stci_d(tempstring, counter);
  1752.     align(tempstring, 4, ' ');
  1753.     Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_2ND, RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 3));
  1754.     Text(MainWindowPtr->RPort, tempstring, 4);
  1755.     // now the second quantity
  1756.     stci_d(tempstring, BONUS_TAIL * multiply);
  1757.     align(tempstring, 4, ' ');
  1758.     Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_3RD, RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 3));
  1759.     Text(MainWindowPtr->RPort, tempstring, 4);
  1760.  
  1761.      levelbonus = (level - 1)          * BONUS_LEVEL  * multiply;
  1762.     numberbonus = worm[player].numbers * BONUS_NUMBER * multiply;
  1763.       timebonus = secondsleft          * BONUS_TIME   * multiply;
  1764.       tailbonus = counter              * BONUS_TAIL   * multiply;
  1765.  
  1766.     for (i = 0; i <= 3; i++)
  1767.         if (worm[i].control != NONE)
  1768.             for (j = 0; j <= LASTOBJECT; j++)
  1769.                 stat(i, j);
  1770.  
  1771.     while (!done)
  1772.     {   while (MsgPtr = (struct IntuiMessage *) GetMsg(MainWindowPtr->UserPort))
  1773.         {   class  = MsgPtr->Class;
  1774.         code   = MsgPtr->Code;
  1775.         qual   = MsgPtr->Qualifier;
  1776.         ReplyMsg((struct Message *) MsgPtr);
  1777.             if (class == IDCMP_RAWKEY && (!(qual & IEQUALIFIER_REPEAT)) && code < KEYUP)
  1778.                 done = TRUE;
  1779.         }
  1780.         if (done)
  1781.     {   wormscore
  1782.             (   player,
  1783.                 ( levelbonus / multiply) +
  1784.                 (numberbonus / multiply) +
  1785.                 (  timebonus / multiply) +
  1786.                 (  tailbonus / multiply)
  1787.             );
  1788.             levelbonus = timebonus = tailbonus = numberbonus = 0;
  1789.         }
  1790.         SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1791.         stci_d(tempstring, levelbonus);
  1792.         align(tempstring, 5, ' ');
  1793.         Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_4TH, RUNDOWNY + RUNDOWNY_2ND                );
  1794.         Text(MainWindowPtr->RPort, tempstring, 5);
  1795.  
  1796.         stci_d(tempstring, numberbonus);
  1797.         align(tempstring, 5, ' ');
  1798.         Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_4TH, RUNDOWNY + RUNDOWNY_2ND +  SQUAREY     );
  1799.         Text(MainWindowPtr->RPort, tempstring, 5);
  1800.  
  1801.         stci_d(tempstring, timebonus);
  1802.         align(tempstring, 5, ' ');
  1803.         Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_4TH, RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 2));
  1804.         Text(MainWindowPtr->RPort, tempstring, 5);
  1805.  
  1806.         stci_d(tempstring, tailbonus);
  1807.         align(tempstring, 5, ' ');
  1808.         Move(MainWindowPtr->RPort, RUNDOWNX + RUNDOWNX_4TH, RUNDOWNY + RUNDOWNY_2ND + (SQUAREY * 3));
  1809.         Text(MainWindowPtr->RPort, tempstring, 5);
  1810.  
  1811.         done = TRUE;
  1812.         if (levelbonus)
  1813.         {   if (levelbonus >= multiply * 2)
  1814.             {   levelbonus -= multiply * 2;
  1815.                 wormscore(player, 2);
  1816.             } else
  1817.             {   levelbonus -= multiply;
  1818.                 wormscore(player, 1);
  1819.             }
  1820.             done = FALSE;
  1821.         }
  1822.         if (numberbonus)
  1823.         {   if (numberbonus >= multiply * 2)
  1824.             {   numberbonus -= multiply * 2;
  1825.                 wormscore(player, 2);
  1826.             } else
  1827.             {   numberbonus -= multiply;
  1828.                 wormscore(player, 1);
  1829.             }
  1830.             done = FALSE;
  1831.         }
  1832.         if (timebonus)
  1833.         {   if (timebonus >= multiply * 2)        
  1834.             {   timebonus -= multiply * 2;
  1835.                 wormscore(player, 2);
  1836.             } else
  1837.             {   timebonus -= multiply;
  1838.                 wormscore(player, 1);
  1839.             }
  1840.             done = FALSE;
  1841.         }
  1842.         if (tailbonus)
  1843.         {   if (tailbonus >= multiply * 2)
  1844.             {   tailbonus -= multiply * 2;
  1845.                 wormscore(player, 2);
  1846.             } else
  1847.             {   tailbonus -= multiply;
  1848.                 wormscore(player, 1);
  1849.             }
  1850.             done = FALSE;
  1851.         }
  1852.         effect(FXCLICK);
  1853.     }
  1854.     clearkybd();
  1855.     effect(FXRIFF);
  1856.     for (i = 0; i <= 3; i++)
  1857.     {   if (worm[i].control == KEYBOARD || worm[i].control == JOYSTICK)
  1858.         {   flag = TRUE;
  1859.             break;
  1860.     }   }
  1861.     if (flag)
  1862.     {   anykey(FALSE);
  1863. }   }
  1864.  
  1865. void say(STRPTR sentence, COLOUR colour)
  1866. {   SWORD length  = (SBYTE) strlen(sentence),
  1867.           centrex;
  1868.  
  1869.     centrex = STARTXPIXEL + ((ENDXPIXEL - STARTXPIXEL) / 2);
  1870.  
  1871.     /* truncate text */
  1872.     if (length > SAYLIMIT)
  1873.     {   *(sentence + SAYLIMIT) = 0;
  1874.         length = SAYLIMIT;
  1875.     }
  1876.  
  1877.     /* clear areas to left and right of text, respectively */
  1878.     SetAPen(MainWindowPtr->RPort, BLACK);
  1879.     RectFill
  1880.     (   MainWindowPtr->RPort,
  1881.         STARTXPIXEL,
  1882.         2,
  1883.         centrex - (length * FONTX / 2),
  1884.         14
  1885.     );
  1886.     RectFill
  1887.     (   MainWindowPtr->RPort,
  1888.         centrex + (length * FONTX / 2) + 1,
  1889.         2,
  1890.         ENDXPIXEL,
  1891.         14
  1892.     );
  1893.     Move(MainWindowPtr->RPort, centrex - (length * FONTX / 2), 2);
  1894.     Draw(MainWindowPtr->RPort, centrex + (length * FONTX / 2), 2);
  1895.  
  1896.     /* render shadow text */
  1897.     SetAPen(MainWindowPtr->RPort, MEDIUMGREY);
  1898.     Move(MainWindowPtr->RPort, centrex - (length * FONTX / 2) + 1, FONTY + 3);
  1899.     Text(MainWindowPtr->RPort, sentence, length);
  1900.     
  1901.     /* render actual text */
  1902.     SetDrMd(MainWindowPtr->RPort, JAM1);
  1903.     SetAPen(MainWindowPtr->RPort, colour);
  1904.     Move(MainWindowPtr->RPort, centrex - (length * FONTX / 2),  FONTY + 3);
  1905.     Text(MainWindowPtr->RPort, sentence, length);
  1906.     SetDrMd(MainWindowPtr->RPort, JAM2);
  1907. }
  1908.     
  1909. void stat(SBYTE player, SBYTE line)
  1910. {   ABOOL print = TRUE;
  1911.     SBYTE i, theline;
  1912.     TEXT  output[7];
  1913.  
  1914.     strcpy(output, "       "); /* 7 spaces */
  1915.     switch (line)
  1916.     {
  1917.     case BONUS:
  1918.         do
  1919.         {   worm[player].oldscore += (LIFEMODULO - (worm[player].oldscore % LIFEMODULO));
  1920.             if (worm[player].score >= worm[player].oldscore)
  1921.             {   worm[player].lives++;
  1922.                 stat(player, LIFE);
  1923.         }   }
  1924.         while (worm[player].score > worm[player].oldscore);
  1925.         worm[player].oldscore = worm[player].score;
  1926.         if (worm[player].multi > 1)
  1927.             SetAPen(MainWindowPtr->RPort, WHITE);
  1928.         else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1929.         stcl_d(output, worm[player].score);
  1930.         theline = 0;
  1931.         for (i = 0; i <= 6; i++)
  1932.             if (!output[i])
  1933.                 output[i] = ' ';
  1934.         // score can't reach 10 million
  1935.     break;
  1936.     case LIFE:
  1937.         if (worm[player].lives > STARTLIVES)
  1938.         {   SetAPen(MainWindowPtr->RPort, WHITE);
  1939.             if (worm[player].lives > LIVESLIMIT)
  1940.                 worm[player].lives = LIVESLIMIT;
  1941.         } else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1942.         stci_d(output, worm[player].lives);
  1943.         for (i = 0; i <= 6; i++)
  1944.             if (!output[i])
  1945.                 output[i] = ' ';
  1946.         theline = 1;
  1947.     break;
  1948.     case BRAKES:
  1949.             if (worm[player].brakes)
  1950.                 SetAPen(MainWindowPtr->RPort, WHITE);
  1951.             else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1952.             if (worm[player].speed == FAST)
  1953.                 strcpy(output, "Fast   ");
  1954.             elif (worm[player].speed == NORMAL)
  1955.                 strcpy(output, "Normal ");
  1956.             elif (worm[player].speed == SLOW)
  1957.                 strcpy(output, "Slow   ");
  1958.             else
  1959.             {   // assert(worm[player].speed == VERYSLOW);
  1960.                 strcpy(output, "V.Slow ");
  1961.             }
  1962.             theline = 2;
  1963.         break;
  1964.     case AMMO:
  1965.             if (worm[player].ammo)
  1966.             {   if (worm[player].ammo > AMMOLIMIT)
  1967.                     worm[player].ammo = AMMOLIMIT;
  1968.                 SetAPen(MainWindowPtr->RPort, WHITE);
  1969.             } else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1970.             stci_d(output, worm[player].ammo);
  1971.             for (i = 0; i <= 6; i++)
  1972.                 if (!output[i])
  1973.                     output[i] = ' ';
  1974.             theline = 3;
  1975.         break;
  1976.     case POWER:
  1977.             switch(worm[player].power)
  1978.             {
  1979.             case 0:
  1980.                 SetAPen(MainWindowPtr->RPort, worm[player].colour);
  1981.                 strcpy(output, "Single ");
  1982.             break;
  1983.             case 2:
  1984.                 SetAPen(MainWindowPtr->RPort, WHITE);
  1985.                 strcpy(output, "Triple ");
  1986.             break;
  1987.             case 4:
  1988.                 SetAPen(MainWindowPtr->RPort, WHITE);
  1989.                 strcpy(output, "Quint. ");
  1990.             break;
  1991.             case 6:
  1992.                 SetAPen(MainWindowPtr->RPort, WHITE);
  1993.                 strcpy(output, "Sept.  ");
  1994.             break;
  1995.             default:
  1996.             break;
  1997.             }
  1998.             theline = 4;
  1999.         break;
  2000.     case ARMOUR:
  2001.             if (worm[player].armour > ARMOURLIMIT)
  2002.                 worm[player].armour = ARMOURLIMIT;
  2003.             if (worm[player].armour > 0)
  2004.                 SetAPen(MainWindowPtr->RPort, WHITE);
  2005.             else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  2006.             stci_d(output, worm[player].armour);
  2007.             for (i = 0; i <= 6; i++)
  2008.                 if (!output[i])
  2009.                     output[i] = ' ';
  2010.             theline = 5;
  2011.         break;
  2012.     case BIAS:
  2013.         if (worm[player].bias > 0)
  2014.         {   if (worm[player].bias > BIASLIMIT)
  2015.                 worm[player].bias = BIASLIMIT;
  2016.             SetAPen(MainWindowPtr->RPort, WHITE);
  2017.         } else SetAPen(MainWindowPtr->RPort, worm[player].colour);
  2018.         stci_d(output, worm[player].bias);
  2019.         for (i = 0; i <= 6; i++)
  2020.             if (!output[i])
  2021.                 output[i] = ' ';
  2022.         theline = 6;
  2023.     break;
  2024.     default:
  2025.         print = FALSE;
  2026.         /* This next line is just to prevent spurious compiler
  2027.         warnings about possibly uninitialized variables */
  2028.         theline = 0;
  2029.     break;
  2030.     }
  2031.  
  2032.     /* Sometimes stat() is called with a valid line, yet an invalid player.
  2033.     Defensive programming currently ensures that this is not a problem, but
  2034.     it would be better to fix the bug. */
  2035.  
  2036.     if (print)
  2037.     {   Move
  2038.         (   MainWindowPtr->RPort,
  2039.             FONTX * 3,
  2040.             STARTYPIXEL + (player * SQUAREX * 10) + 8 + (theline * SQUAREY)
  2041.         );
  2042.         Text(MainWindowPtr->RPort, output, 7);
  2043. }   }
  2044.  
  2045. void stopfx(void)
  2046. {   SBYTE i;
  2047.  
  2048.     if (mode == FX)
  2049.         for (i = 0; i <= 3; i++)
  2050.             // CheckIO() returns NULL if complete, pointer if incomplete? (!)
  2051.             if (eversent[i] && (!(CheckIO((struct IORequest *) AudioRqPtr[i]))))
  2052.             {   AbortIO((struct IORequest *) AudioRqPtr[i]);
  2053.                 WaitIO((struct IORequest *) AudioRqPtr[i]);
  2054. }           }
  2055.  
  2056. void titlescreen(void)
  2057. {   SBYTE                object = LASTOBJECT, player, i;
  2058.     SWORD                descx = FIRSTDESCX;
  2059.     ULONG                class;
  2060.     UWORD                code, qual;
  2061.     TEXT                 tempstring[8];
  2062.     struct IntuiMessage* MsgPtr;
  2063.     struct Gadget*       WhichGadgetPtr;
  2064.     struct MenuItem*     ItemPtr;
  2065.  
  2066.     effect(FXLAUNCH);
  2067.     Forbid();
  2068.     MainWindowPtr->Flags &= ~WFLG_RMBTRAP;
  2069.     Permit();
  2070.     clearscreen();
  2071.  
  2072.     SetMenuStrip(MainWindowPtr, MenuPtr);
  2073.     for (player = 0; player <= 3; player++)
  2074.     {   GT_SetGadgetAttrs(CycleGadgetPtr[player], MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);
  2075.     }
  2076.     GT_SetGadgetAttrs(RandomGadgetPtr, MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);
  2077.     GT_SetGadgetAttrs(FEGadgetPtr,     MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);
  2078.     GT_SetGadgetAttrs(PlayGadgetPtr,   MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);
  2079.     SetAPen(MainWindowPtr->RPort, MEDIUMGREY);
  2080.     SetDrMd(MainWindowPtr->RPort, JAM1);
  2081.     Move(MainWindowPtr->RPort, 370 + 50 - ((14 + 1) * SQUAREX), TSOFFSET + 170 + 7);
  2082.     Text(MainWindowPtr->RPort, "Shuffle Levels?", 15);
  2083.     Move(MainWindowPtr->RPort, 370 + 50 - ((14 + 1) * SQUAREX), TSOFFSET + 170 + 7 + 1);
  2084.     Text(MainWindowPtr->RPort, "_", 1);
  2085.     strcpy(tempstring, "Worm  :");
  2086.     for (player = 0; player <= 3; player++)
  2087.     {   tempstring[5] = '1' + player;
  2088.         SetAPen(MainWindowPtr->RPort, worm[player].dimcolour);
  2089.         Move(MainWindowPtr->RPort, 320 - ((7 + 1) * SQUAREX),  TSOFFSET + 200 + 9 + (player * 16)    );
  2090.         Text(MainWindowPtr->RPort, tempstring, 7);
  2091.         Move(MainWindowPtr->RPort, 300 - ((2 + 1) * SQUAREX),  TSOFFSET + 200 + 9 + (player * 16) + 1);
  2092.         Text(MainWindowPtr->RPort, "_", 1);
  2093.     }
  2094.     RefreshGList(FEGadgetPtr,   MainWindowPtr, NULL, 1);
  2095.     RefreshGList(PlayGadgetPtr, MainWindowPtr, NULL, 1);
  2096.  
  2097.     clearkybd();
  2098.     hiscores();
  2099.     hiscorenames();
  2100.  
  2101.     SetDrMd(MainWindowPtr->RPort, JAM2);
  2102.     DrawImage(MainWindowPtr->RPort, &Logo, 168, 48);
  2103.  
  2104.     do
  2105.     {   TimerRqPtr->tr_node.io_Command  = TR_ADDREQUEST;
  2106.         TimerRqPtr->tr_time.tv_secs     = 0;
  2107.         TimerRqPtr->tr_time.tv_micro    = ANIMDELAY;
  2108.         SendIO((struct IORequest *) TimerRqPtr);
  2109.  
  2110.         if (descx == FIRSTDESCX)
  2111.         {   if (++object > LASTOBJECT)
  2112.             {   object = 0;
  2113.             }
  2114.             say(objectdesc[object], WHITE);
  2115.             Image.ImageData = ImageData[object];
  2116.             DrawImage(MainWindowPtr->RPort, &Image, SECONDDESCX, DESCY);
  2117.         }
  2118.         SetAPen(MainWindowPtr->RPort, BLACK);
  2119.         Move(MainWindowPtr->RPort, descx - 1, DESCY - 1);
  2120.         Draw(MainWindowPtr->RPort, descx - 1, DESCY + 1 + SQUAREY);
  2121.         Move(MainWindowPtr->RPort, descx + 1 + SQUAREX, DESCY - 1);
  2122.         Draw(MainWindowPtr->RPort, descx + 1 + SQUAREX, DESCY + 1 + SQUAREY);
  2123.         Image.ImageData = ImageData[object];
  2124.         DrawImage(MainWindowPtr->RPort, &Image, descx, DESCY);
  2125.         if (++descx > SECONDDESCX)
  2126.         {   descx = FIRSTDESCX;
  2127.         }
  2128.  
  2129.         while (MsgPtr = (struct IntuiMessage *) GT_GetIMsg(MainWindowPtr->UserPort))
  2130.         {   WhichGadgetPtr = (struct Gadget *) MsgPtr->IAddress;
  2131.             class = MsgPtr->Class;
  2132.             code  = MsgPtr->Code;
  2133.             qual  = MsgPtr->Qualifier;
  2134.             GT_ReplyIMsg(MsgPtr);
  2135.             switch (class)
  2136.             {
  2137.             case IDCMP_RAWKEY:
  2138.                 if (!(qual & IEQUALIFIER_REPEAT))
  2139.                 {   switch (code)
  2140.                     {
  2141.                     case F:
  2142.                         toggle(F);
  2143.                     break;
  2144.                     case M:
  2145.                         toggle(M);
  2146.                     break;
  2147.                     case SPACEBAR:
  2148.                         a = FIELDEDIT;
  2149.                     break;
  2150.                     case F1:
  2151.                     case ALPHAONE:
  2152.                     case NUMERICONE:
  2153.                         effect(FXCLICK);
  2154.                         if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  2155.                         {   if (--worm[0].control < 0)
  2156.                                 worm[0].control = 4;
  2157.                         } else
  2158.                         {   if (++worm[0].control > 4)
  2159.                                 worm[0].control = 0;
  2160.                         }
  2161.                         GT_SetGadgetAttrs(CycleGadgetPtr[0], MainWindowPtr, NULL, GTCY_Active, worm[0].control, TAG_DONE);
  2162.                     break;
  2163.                     case F2:
  2164.                     case ALPHATWO:
  2165.                     case NUMERICTWO:
  2166.                         effect(FXCLICK);
  2167.                         if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  2168.                         {   if (--worm[1].control < 0)
  2169.                                 worm[1].control = 4;
  2170.                         } else
  2171.                         {   if (++worm[1].control > 4)
  2172.                                 worm[1].control = 0;
  2173.                         }
  2174.                         GT_SetGadgetAttrs(CycleGadgetPtr[1], MainWindowPtr, NULL, GTCY_Active, worm[1].control, TAG_DONE);
  2175.                     break;
  2176.                     case F3:
  2177.                     case ALPHATHREE:
  2178.                     case NUMERICTHREE:
  2179.                         effect(FXCLICK);
  2180.                         if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  2181.                         {   if (worm[2].control > 0)
  2182.                             {   worm[2].control--;
  2183.                             } else worm[2].control = 3;
  2184.                         } else
  2185.                         {   if (worm[2].control < 3)
  2186.                             {   worm[2].control++;
  2187.                             } else worm[2].control = 0;
  2188.                         }
  2189.                         GT_SetGadgetAttrs(CycleGadgetPtr[2], MainWindowPtr, NULL, GTCY_Active, worm[2].control, TAG_DONE);
  2190.                     break;
  2191.                     case F4:
  2192.                     case ALPHAFOUR:
  2193.                     case NUMERICFOUR:
  2194.                         effect(FXCLICK);
  2195.                         if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
  2196.                         {   if (--worm[3].control < 0)
  2197.                                 worm[3].control = 3;
  2198.                         } else
  2199.                         {   if (++worm[3].control > 3)
  2200.                                 worm[3].control = 0;
  2201.                         }
  2202.                         GT_SetGadgetAttrs(CycleGadgetPtr[3], MainWindowPtr, NULL, GTCY_Active, worm[3].control, TAG_DONE);
  2203.                     break;
  2204.                     case RETURN:
  2205.                     case ENTER:
  2206.                         a = PLAYGAME;
  2207.                     break;
  2208.                     case S:
  2209.                         randomflag = !randomflag;
  2210.                         GT_SetGadgetAttrs(RandomGadgetPtr, MainWindowPtr, NULL, GTCB_Checked, randomflag, TAG_DONE);
  2211.                     break;
  2212.                     case ESCAPE:
  2213.                         if (verify())
  2214.                             cleanexit(EXIT_SUCCESS);
  2215.                     break;
  2216.                     case HELP:
  2217.                         helpabout();
  2218.                     break;
  2219.                     default:
  2220.                     break;
  2221.                 }   }
  2222.             break;
  2223.             case IDCMP_MENUPICK:
  2224.                 while (code != MENUNULL)
  2225.                 {   ItemPtr = ItemAddress(MenuPtr, code);
  2226.  
  2227.                     switch (MENUNUM(code))
  2228.                     {
  2229.                     case MN_PROJECT:
  2230.                         switch (ITEMNUM(code))
  2231.                         {
  2232.                         case IN_NEW:
  2233.                             newfields();
  2234.                             say("New done.", WHITE);
  2235.                             anykey(TRUE);
  2236.                             say(objectdesc[object], WHITE);
  2237.                         break;
  2238.                         case IN_OPEN:
  2239.                             fileopen(FALSE);
  2240.                             say(objectdesc[object], WHITE);
  2241.                         break;
  2242.                         case IN_REVERT:
  2243.                             fileopen(TRUE);
  2244.                             say(objectdesc[object], WHITE);
  2245.                         break;
  2246.                         case IN_SAVE:
  2247.                             filesaveas(FALSE);
  2248.                             say(objectdesc[object], WHITE);
  2249.                         break;
  2250.                         case IN_SAVEAS:
  2251.                             filesaveas(TRUE);
  2252.                             say(objectdesc[object], WHITE);
  2253.                         break;
  2254.                         case IN_PROJECTDELETE:
  2255.                             filedelete();
  2256.                         break;
  2257.                         case IN_QUIT:
  2258.                             if (verify())
  2259.                                 cleanexit(EXIT_SUCCESS);
  2260.                         break;
  2261.                         default:
  2262.                         break;
  2263.                         }
  2264.                     break;
  2265.                     case MN_SETTINGS:
  2266.                         switch(ITEMNUM(code))
  2267.                         {
  2268.                         case IN_ANIMATIONS:
  2269.                             if (ItemPtr->Flags & CHECKED)
  2270.                             {   anims = TRUE;
  2271.                             } else
  2272.                             {   anims = FALSE;
  2273.                             }
  2274.                         break;
  2275.                         case IN_CREATEICONS:
  2276.                             if (ItemPtr->Flags & CHECKED)
  2277.                             {   icons = TRUE;
  2278.                             } else
  2279.                             {   icons = FALSE;
  2280.                             }
  2281.                         break;
  2282.                         case IN_THICKTAILS:
  2283.                             if (ItemPtr->Flags & CHECKED)
  2284.                             {   thick = TRUE;
  2285.                             } else
  2286.                             {   thick = FALSE;
  2287.                             }
  2288.                         break;
  2289.                         default:
  2290.                         break;
  2291.                         }
  2292.                     break;
  2293.                     case MN_HELP:
  2294.                         switch(ITEMNUM(code))
  2295.                         {
  2296.                         case IN_ABOUT:
  2297.                             helpabout();
  2298.                         break;
  2299.                         case IN_CREATURES:
  2300.                             help(ORB);
  2301.                         break;
  2302.                         case IN_OBJECTS:
  2303.                             help(AFFIXER);
  2304.                         break;
  2305.                         default:
  2306.                         break;
  2307.                         }
  2308.                     break;
  2309.                     default:
  2310.                     break;
  2311.                     }
  2312.                     code = ItemPtr->NextSelect;
  2313.                 }
  2314.             break;
  2315.             case IDCMP_MOUSEBUTTONS:
  2316.                 if (code == SELECTDOWN)
  2317.                 {   if (ignore)
  2318.                     {   ignore = FALSE;
  2319.                     } else
  2320.                     {   a = PLAYGAME;
  2321.                 }   }
  2322.             break;
  2323.             case IDCMP_REFRESHWINDOW:
  2324.                 GT_BeginRefresh(MainWindowPtr);
  2325.                 GT_EndRefresh(MainWindowPtr, TRUE);
  2326.             break;
  2327.             case IDCMP_GADGETUP:
  2328.                 if (WhichGadgetPtr == RandomGadgetPtr)
  2329.                 {   randomflag = !randomflag;
  2330.                 } elif (WhichGadgetPtr == PlayGadgetPtr)
  2331.                 {   a = PLAYGAME;
  2332.                 } elif (WhichGadgetPtr == FEGadgetPtr)
  2333.                 {   a = FIELDEDIT;
  2334.                 } else
  2335.                 {   for (player = 0; player <= 3; player++)
  2336.                     {   if (WhichGadgetPtr == CycleGadgetPtr[player])
  2337.                         {   worm[player].control = code;
  2338.                             break;
  2339.                 }   }   }
  2340.             break;
  2341.             case IDCMP_ACTIVEWINDOW:
  2342.                 ignore = TRUE;
  2343.             break;
  2344.             case IDCMP_CLOSEWINDOW:
  2345.                 cleanexit(EXIT_SUCCESS);
  2346.             break;
  2347.             case IDCMP_INTUITICKS:
  2348.                 for (i = 0; i <= HISCORES; i++)
  2349.                 {   if (anims && hiscore[i].player != -1)
  2350.                     {   Image.ImageData = ImageData[missileframes[hiscore[i].player][hiframe]];
  2351.                         DrawImage(MainWindowPtr->RPort, &Image, 540, TSOFFSET + 3 + (i * HISCOREDISTANCE));
  2352.                         if (++hiframe > MISSILEFRAMES)
  2353.                         {   hiframe = 0;
  2354.                 }   }   }
  2355.             break;
  2356.             default:
  2357.                 ; /* IDCMP_MENUVERIFY */
  2358.             break;
  2359.         }   }
  2360.  
  2361.         if (firebutton())
  2362.         {   a = PLAYGAME;
  2363.         }
  2364.         if (a == PLAYGAME)
  2365.         {   if (worm[0].control == NONE && worm[1].control == NONE && worm[2].control == NONE && worm[3].control == NONE)
  2366.             {   say("No worms active!", WHITE);
  2367.                 anykey(TRUE);
  2368.                 a = GAMEOVER;
  2369.             } elif
  2370.             (   !LowLevelBase &&
  2371.                 (worm[0].control == GAMEPAD
  2372.               || worm[1].control == GAMEPAD
  2373.               || worm[2].control == GAMEPAD
  2374.               || worm[3].control == GAMEPAD
  2375.             )   )
  2376.             {   say("Need OS3.1+ for gamepad!", WHITE);
  2377.                 anykey(TRUE);
  2378.                 a = GAMEOVER;
  2379.         }   }
  2380.         // CheckIO() returns NULL if complete, pointer if incomplete.
  2381.         if (CheckIO((struct IORequest *) TimerRqPtr))
  2382.             draw(CLOCKICON, ICONY, CLOCK);
  2383.         else draw(CLOCKICON, ICONY, BLACKENED);
  2384.         WaitIO((struct IORequest *) TimerRqPtr);
  2385.     } while (a == GAMEOVER);
  2386.  
  2387.     for (player = 0; player <= 3; player++)
  2388.     {   GT_SetGadgetAttrs(CycleGadgetPtr[player], MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
  2389.     }
  2390.     GT_SetGadgetAttrs(RandomGadgetPtr, MainWindowPtr, NULL, GA_Disabled, TRUE, TAG_DONE);
  2391.     GT_SetGadgetAttrs(FEGadgetPtr,     MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);
  2392.     GT_SetGadgetAttrs(PlayGadgetPtr,   MainWindowPtr, NULL, GA_Disabled, FALSE, TAG_DONE);
  2393.     Image.ImageData = ImageData[BLACKENED];
  2394.     DrawImage(MainWindowPtr->RPort, &Image, FIRSTDESCX, DESCY);
  2395.     DrawImage(MainWindowPtr->RPort, &Image, SECONDDESCX, DESCY);
  2396.  
  2397.     if (a == FIELDEDIT)
  2398.     {   fieldedit();
  2399.     } else
  2400.     {   turbo = FALSE;
  2401.         newgame();
  2402.         clearkybd();
  2403.         Forbid();
  2404.         MainWindowPtr->Flags |= WFLG_RMBTRAP;
  2405.         Permit();
  2406. }   }
  2407.  
  2408. AGLOBAL void toggle(SBYTE key)
  2409. {   PERSIST ABOOL songstarted = FALSE;
  2410.  
  2411.     switch(key)
  2412.     {
  2413.     case F:
  2414.         if (mode == FX) /* F in FX mode: no sound */
  2415.         {   freefx();
  2416.             mode = FALSE;
  2417.             draw(MUSICICON, ICONY, BLACKENED);
  2418.         } else if (fxable != FAILED) /* F otherwise: change to FX mode */
  2419.         {   if (mode == MUSIC) /* stop any music that is playing */
  2420.             {   StopPlayer();
  2421.                 FreePlayer();
  2422.             }
  2423.             if (fxable == DEFER) /* load samples if needed */
  2424.                 loadthefx();
  2425.             if (fxable == SUCCEEDED) /* if we have samples in memory */
  2426.             {   if (beginfx())
  2427.                 {   mode = FX;
  2428.                     effect(FXLAUNCH);
  2429.                     draw(MUSICICON, ICONY, FX);
  2430.         }   }   }
  2431.     break;
  2432.     case M:
  2433.         if (mode == MUSIC) /* M in MUSIC mode: no sound */
  2434.         {   StopPlayer();
  2435.             FreePlayer();
  2436.             mode = FALSE;
  2437.             draw(MUSICICON, ICONY, BLACKENED);
  2438.         } else if (musicable != FAILED) /* M otherwise: change to music mode */
  2439.         {   if (mode == FX) /* stop any samples that are playing */
  2440.                 freefx();
  2441.             /* Of course, these statements are ordered in this
  2442.             way for a reason, so don't change it. :-) */
  2443.             if (musicable == DEFER)
  2444.                 loadthemusic();
  2445.             if (musicable == SUCCEEDED)
  2446.             {   if (GetPlayer(0))
  2447.                 {   say("No channels for music!", RED);
  2448.                     anykey(TRUE);
  2449.                     mode = FALSE;
  2450.                     draw(MUSICICON, ICONY, BLACKENED);
  2451.                 } else
  2452.                 {   if (songstarted)
  2453.                         ContModule(SongPtr);
  2454.                     else
  2455.                     {   PlayModule(SongPtr);
  2456.                         songstarted = TRUE;
  2457.                     }
  2458.                     mode = MUSIC;
  2459.                     draw(MUSICICON, ICONY, MUSIC);
  2460.         }   }   }
  2461.     break;
  2462.     default:
  2463.         flash(2);
  2464.         // assert(0);
  2465.     break;
  2466. }   }
  2467.  
  2468. ABOOL verify(void)
  2469. {    pausetimer();
  2470.     if (modified && (EasyRequest(MainWindowPtr, &EasyStruct, NULL) == 0))
  2471.         return FALSE;
  2472.     else return TRUE;
  2473.     unpausetimer();
  2474. }
  2475.  
  2476. void waitasec(void)
  2477. {   Delay(50);
  2478. }
  2479.  
  2480. void systemsetup(void)
  2481. {    worm[0].control    = NONE;
  2482.     worm[1].control    = KEYBOARD;
  2483.     worm[2].control    = NONE;
  2484.     worm[3].control = AMIGA;
  2485. }
  2486.  
  2487. ABOOL ZOpen(STRPTR fieldname, ABOOL write)
  2488. {   if (!write)
  2489.         if (FilePtr = Open(fieldname, MODE_OLDFILE))
  2490.             return TRUE;
  2491.         else return FALSE;
  2492.     else
  2493.         if (FilePtr = Open(fieldname, MODE_NEWFILE))
  2494.             return TRUE;
  2495.         else return FALSE;
  2496. }
  2497. ABOOL ZRead(STRPTR IOBuffer, ULONG length)
  2498. {   if (Read(FilePtr, IOBuffer, length) == length)
  2499.         return TRUE;
  2500.     else return FALSE;
  2501. }
  2502. ABOOL ZWrite(STRPTR IOBuffer, ULONG length)
  2503. {   if (Write(FilePtr, IOBuffer, length) == length)
  2504.         return TRUE;
  2505.     else return FALSE;
  2506. }
  2507. ABOOL ZClose(void)
  2508. {   if (Close(FilePtr))
  2509.     {   FilePtr = NULL;
  2510.         return TRUE;
  2511.     } else
  2512.     {   /* "If Close() returns DOSFALSE, the user has already cancelled an
  2513.         error requester and the function has already freed the FileHandle
  2514.         (and even marked it so any attempt to close it again will bring up
  2515.         the "Software Failure" requester). Therefore FilePtr should be set
  2516.         to zero in any case." - Jilles Tjoelker. */
  2517.  
  2518.         FilePtr = NULL;
  2519.         return FALSE;
  2520. }   }
  2521.  
  2522. void timing(void)
  2523. {   ;
  2524. }
  2525.  
  2526. MODULE void loadthefx(void)
  2527. {   UBYTE*        p8data;
  2528.     TEXT          saystring[SAYLIMIT + 1];
  2529.     SBYTE         code = 0, i,
  2530.                   iobuffer[8]; /* buffer for 8SVX.VHDR  */
  2531.     SBYTE*        psample[2];  /* sample pointers */
  2532.     struct Chunk* p8Chunk;     /* pointers for 8SVX parsing */
  2533.     Voice8Header* pVoice8Header = NULL; // only set to NULL to avoid spurious warnings
  2534.     ULONG         rd8count;
  2535.  
  2536.     say("Loading sound effects...", WHITE);
  2537.     fxable = SUCCEEDED;
  2538.  
  2539.     for (i = 0; i <= SAMPLES; i++)
  2540.         samp[i].base = NULL;
  2541.  
  2542.     for (i = 0; i <= SAMPLES; i++)
  2543.     {   if (!(FilePtr = Open(samp[i].filename, MODE_OLDFILE)))
  2544.             code = 1;                               /* can't open file */
  2545.         else
  2546.         {   rd8count = Read(FilePtr, iobuffer, 8L);
  2547.             if (rd8count == -1)
  2548.                 code = 2;                           /* can't read file */
  2549.             elif (rd8count < 8)
  2550.                 code = 3;                           /* not an IFF 8SVX; too short */
  2551.             else
  2552.             {   p8Chunk = (struct Chunk *) iobuffer;
  2553.                 if (p8Chunk->ckID != ID_FORM)
  2554.                     code = 4;                       /* not an IFF FORM */
  2555.                 elif (!(fbase = (UBYTE *) AllocMem(fsize = p8Chunk->ckSize, MEMF_PUBLIC | MEMF_CLEAR)))
  2556.                     code = 5;                       /* no memory for read */
  2557.                 else
  2558.                 {   p8data = fbase;
  2559.                     rd8count = Read(FilePtr, p8data, p8Chunk->ckSize);
  2560.                     if (rd8count == -1)
  2561.                         code = 6;                   /* read error */
  2562.                     elif (rd8count < p8Chunk->ckSize)
  2563.                         code = 7;                   /* malformed IFF; too short */
  2564.                     elif (MAKE_ID(*p8data, *(p8data + 1), *(p8data + 2), *(p8data + 3)) != ID_8SVX)
  2565.                         code = 8;                   /* not an IFF 8SVX */
  2566.                     else
  2567.                     {   p8data = p8data + 4;
  2568.                         while (p8data < fbase + fsize)
  2569.                         {   p8Chunk = (struct Chunk *) p8data;
  2570.                             switch(p8Chunk->ckID)
  2571.                             {
  2572.                             case ID_VHDR:
  2573.                                 pVoice8Header     = (Voice8Header *) (p8data + 8L);
  2574.                             break;
  2575.                             case ID_BODY:
  2576.                                 psample[0]        = (SBYTE *) (p8data + 8L);
  2577.                                 psample[1]        = psample[0] + pVoice8Header->oneShotHiSamples;
  2578.                                 samp[i].length[0] = (ULONG) pVoice8Header->oneShotHiSamples;
  2579.                                 samp[i].length[1] = (ULONG) pVoice8Header->repeatHiSamples;
  2580.  
  2581.                                 /* To grab the volume level from the IFF
  2582.                                 8SVX file itself, add this line here:
  2583.  
  2584.                                 samp[i].volume    = (SBYTE) (pVoice8Header->volume / 156); */
  2585.                             break;
  2586.                             default:
  2587.                             break;
  2588.                             }
  2589.                             p8data += 8L + p8Chunk->ckSize;
  2590.                             if (p8Chunk->ckSize & 1L == 1)
  2591.                                 p8data++;
  2592.                         }
  2593.                         if (samp[i].length[0] == 0)
  2594.                             samp[i].bank = 1;
  2595.                         else samp[i].bank = 0;
  2596.                         if (samp[i].length[samp[i].bank] <= 102400)
  2597.                             samp[i].size = samp[i].length[samp[i].bank];
  2598.                         else samp[i].size = 102400;
  2599.                         samp[i].base = (UBYTE *) AllocMem(samp[i].size, MEMF_CHIP | MEMF_CLEAR);
  2600.                         if (!samp[i].base)
  2601.                             code = 9; /* no chip memory */
  2602.                         else
  2603.                         {   CopyMem(psample[samp[i].bank], samp[i].base, samp[i].size);
  2604.                             psample[samp[i].bank] += samp[i].size;
  2605.                             if (GfxBase->DisplayFlags & PAL) // PAL clock
  2606.                             {   samp[i].speed = PALCLOCK / pVoice8Header->samplesPerSec;
  2607.                             } else // NTSC clock
  2608.                             {   samp[i].speed = NTSCCLOCK / pVoice8Header->samplesPerSec;
  2609.                             }
  2610.  
  2611. /* "The maximum frequency achievable depends on the screen rate because
  2612. the Paula audio DMA period depends on the horizontal refresh rate, but the
  2613. actual clock rate of Paula by which the audio DA converters are driven
  2614. depends really only on the clock rate.
  2615.  
  2616. It's fixed for either PAL or NTSC machines, NOT for PAL or NTSC screen
  2617. modes. Hence just the two values that are given in the RKRMs are
  2618. completely sufficient. Note that running an NTSC screen on a PAL machine
  2619. does not change the system clock and hence neither the audio frequency.
  2620. It changes horizontal timing and vertical timing, and hence the minimal
  2621. audio period." - Thomas `Thor' Richter. */
  2622.  
  2623.                             if (fbase)
  2624.                             {   FreeMem(fbase, fsize);
  2625.                                 fbase = NULL;
  2626.                             }
  2627.                             if (FilePtr)
  2628.                             {   Close(FilePtr);
  2629.                                 FilePtr = NULL;
  2630.         }   }   }   }   }   }
  2631.         if (code)
  2632.         {   freefx();
  2633.             fxable = FAILED;
  2634.             strcpy(saystring, samp[i].filename);
  2635.             strcat(saystring, ": ");
  2636.             strcat(saystring, sfxerror[code]);
  2637.             say(saystring, RED);
  2638.             anykey(TRUE);
  2639.             break;
  2640. }   }   }
  2641.  
  2642. MODULE void loadthemusic(void)
  2643. {    if (!(MEDPlayerBase = (struct MEDPlayerBase *) OpenLibrary("medplayer.library", 0L)))
  2644.     {    say("Can't open MEDPlayer.library!", RED);
  2645.         anykey(TRUE);
  2646.     } else
  2647.     {    say("Loading music...", WHITE);
  2648.         if (SongPtr = (struct MMD0 *) LoadModule("PROGDIR:WormWars.MED"))
  2649.                         musicable = SUCCEEDED;
  2650.         else
  2651.         {    say("Can't load music!", RED);
  2652.             anykey(TRUE);
  2653. }    }    }
  2654.  
  2655. void clearscreen(void)
  2656. {   SBYTE i;
  2657.  
  2658.     SetAPen(MainWindowPtr->RPort, BLACK);
  2659.     RectFill(MainWindowPtr->RPort, 0, 0, ENTIREXPIXEL, ENTIREYPIXEL);
  2660.  
  2661.     if (mode == MUSIC)
  2662.         draw(MUSICICON, ICONY, MUSIC);
  2663.     elif (mode == FX)
  2664.         draw(MUSICICON, ICONY, FX);
  2665.     else draw(MUSICICON, ICONY, BLACKENED);
  2666.     draw(CLOCKICON, ICONY, CLOCK);
  2667.  
  2668.     if (a != FIELDEDIT)
  2669.     {   for (i = 0; i <= 3; i++)
  2670.     {   if (worm[i].control != NONE)
  2671.             {   draw(-6, (i * 10)    , BONUS);
  2672.                 draw(-6, (i * 10) + 1, LIFE);
  2673.                 draw(-6, (i * 10) + 2, BRAKES);
  2674.                 draw(-6, (i * 10) + 3, AMMO);
  2675.                 draw(-6, (i * 10) + 4, POWER);
  2676.                 draw(-6, (i * 10) + 5, ARMOUR);
  2677.                 draw(-6, (i * 10) + 6, BIAS);
  2678.         }   }
  2679.         for (i = 0; i <= 3; i++)
  2680.         {   icon(i, REMNANTS);
  2681.             icon(i, AFFIXER);
  2682.             icon(i, SIDESHOT);
  2683.             icon(i, PUSHER);
  2684.             icon(i, GLOW);
  2685.             icon(i, CUTTER);
  2686.             icon(i, ENCLOSER);
  2687. }   }   }
  2688.  
  2689. void datestamp(void)
  2690. {   ULONG            seconds, micros;
  2691.     struct ClockData Date;
  2692.     TEXT             temp[5];
  2693.  
  2694.     CurrentTime(&seconds, µs);
  2695.     Amiga2Date(seconds, &Date);
  2696.     stci_d(times, Date.hour);            /* hh */
  2697.     align(times, 2, ' ');
  2698.     times[2] = ':';                     /* hh: */
  2699.     times[3] = 0;
  2700.     stci_d(temp, Date.min);
  2701.     align(temp, 2, '0');
  2702.     temp[2] = 0;
  2703.     strcat(times, temp);                /* hh:mm */
  2704.  
  2705.     stci_d(date, Date.mday);            /* dd */
  2706.     align(date, 2, ' ');
  2707.     date[2] = '/';
  2708.     date[3] = 0;                        /* dd/ */
  2709.     stci_d(temp, Date.month);
  2710.     align(temp, 2, ' ');
  2711.     temp[2] = 0;
  2712.     strcat(date, temp);                    /* dd/mm */
  2713.     strcat(date, "/");                    /* dd/mm/ */
  2714.     stci_d(temp, Date.year);
  2715.     temp[0] = temp[2];
  2716.     temp[1] = temp[3];
  2717.     temp[2] = 0;
  2718.     strcat(date, temp);                    /* dd/mm/yy */
  2719. }
  2720.  
  2721. void turborender(void)
  2722. {   UBYTE random = rand() % 2;
  2723.     SBYTE x, y;
  2724.  
  2725.     if (a != PLAYGAME || (!level) || !(randomflag))
  2726.         sourcelevel = level;
  2727.  
  2728.     switch(random)
  2729.     {
  2730.     case 0:
  2731.         for (x = 0; x <= CENTREX + 1; x++)
  2732.             for (y = 0; y <= FIELDY / 2; y++)
  2733.             {   draw(x, y, board[sourcelevel][x][y]);
  2734.                 draw(FIELDX - x, y, board[sourcelevel][FIELDX - x][y]);
  2735.                 draw(x, FIELDY - y, board[sourcelevel][x][FIELDY - y]);
  2736.                 draw(FIELDX - x, FIELDY - y, board[sourcelevel][FIELDX - x][FIELDY - y]);
  2737.             }
  2738.     break;
  2739.     case 1:
  2740.         for (y = 0; y <= FIELDY / 2; y++)
  2741.             for (x = 0; x <= CENTREX + 1; x++)
  2742.             {   draw(x, y, board[sourcelevel][x][y]);
  2743.                 draw(FIELDX - x, y, board[sourcelevel][FIELDX - x][y]);
  2744.                 draw(x, FIELDY - y, board[sourcelevel][x][FIELDY - y]);
  2745.                 draw(FIELDX - x, FIELDY - y, board[sourcelevel][FIELDX - x][FIELDY - y]);
  2746.             }
  2747.     break;
  2748.     default:
  2749.         assert(0);
  2750.     break;
  2751.     }
  2752.  
  2753.     if (a == FIELDEDIT)
  2754.     {   draw(startx[sourcelevel], starty[sourcelevel], START);
  2755.     } else
  2756.     {   for (y = 0; y <= FIELDY; y++)
  2757.         {   draw(ARROWX, y, BLACKENED);
  2758.     }   }
  2759.  
  2760.     SetAPen(MainWindowPtr->RPort, MEDIUMGREY);
  2761.     Move(MainWindowPtr->RPort, STARTXPIXEL    ,   ENDYPIXEL + 1);
  2762.     Draw(MainWindowPtr->RPort,   ENDXPIXEL    ,   ENDYPIXEL + 1);
  2763.     Move(MainWindowPtr->RPort,   ENDXPIXEL + 1, STARTYPIXEL    );
  2764.     Draw(MainWindowPtr->RPort,   ENDXPIXEL + 1,   ENDYPIXEL + 1);
  2765. }
  2766.  
  2767. MODULE void parsewb(void)
  2768. {   struct DiskObject* DiskObject;
  2769.     char**             ToolArray;
  2770.     char*              s;
  2771.  
  2772.     if ((*WBArg->wa_Name) && (DiskObject = GetDiskObject(WBArg->wa_Name)))
  2773.     {   ToolArray = (char **) DiskObject->do_ToolTypes;
  2774.  
  2775.         if (s = (char *) FindToolType(ToolArray, "NOPRELOAD"))
  2776.         {   fxable = musicable = DEFER;
  2777.         }
  2778.         if (s = (char *) FindToolType(ToolArray, "NOANIMS"))
  2779.         {   anims = FALSE;
  2780.         }
  2781.         if (s = (char *) FindToolType(ToolArray, "NOICONS"))
  2782.         {   icons = FALSE;
  2783.         }
  2784.         if (s = (char *) FindToolType(ToolArray, "SHUFFLE"))
  2785.         {   randomflag = TRUE;
  2786.         }
  2787.         if (s = (STRPTR) FindToolType(ToolArray, "QUIET"))
  2788.         {   quiet = TRUE;
  2789.         }
  2790.         if (s = (char *) FindToolType(ToolArray, "THICKTAILS"))
  2791.         {   thick = TRUE;
  2792.         }
  2793.         if (s = (char *) FindToolType(ToolArray, "FILE"))
  2794.         {   strcpy(pathname, WBArg->wa_Name);
  2795.         }
  2796.         if (s = (char *) FindToolType(ToolArray, "GREEN"))
  2797.         {   if (MatchToolValue(s, "KYBD"))
  2798.             {   worm[0].control = KEYBOARD;
  2799.             } elif (MatchToolValue(s, "JOY"))
  2800.             {   worm[0].control = JOYSTICK;
  2801.             } elif (MatchToolValue(s, "GAMEPAD"))
  2802.             {   worm[0].control = GAMEPAD;
  2803.             } elif (MatchToolValue(s, "AMIGA"))
  2804.             {   worm[0].control = AMIGA;
  2805.             } elif (MatchToolValue(s, "NONE"))
  2806.             {   worm[0].control = NONE;
  2807.         }   }
  2808.         if (s = (char *) FindToolType(ToolArray, "RED"))
  2809.         {   if (MatchToolValue(s, "KYBD"))
  2810.             {   worm[1].control = KEYBOARD;
  2811.             } elif (MatchToolValue(s, "JOY"))
  2812.             {   worm[1].control = JOYSTICK;
  2813.             } elif (MatchToolValue(s, "GAMEPAD"))
  2814.             {   worm[1].control = GAMEPAD;
  2815.             } elif (MatchToolValue(s, "AMIGA"))
  2816.             {   worm[1].control = AMIGA;
  2817.             } elif (MatchToolValue(s, "NONE"))
  2818.             {   worm[1].control = NONE;
  2819.         }   }
  2820.         if (s = (char *) FindToolType(ToolArray, "BLUE"))
  2821.         {   if (MatchToolValue(s, "JOY"))
  2822.             {   worm[2].control = JOYSTICK;
  2823.             } elif (MatchToolValue(s, "GAMEPAD"))
  2824.             {   worm[2].control = GAMEPAD;
  2825.             } elif (MatchToolValue(s, "AMIGA"))
  2826.             {   worm[2].control = AMIGA;
  2827.             } elif (MatchToolValue(s, "NONE"))
  2828.             {   worm[2].control = NONE;
  2829.         }   }
  2830.         if (s = (char *) FindToolType(ToolArray, "YELLOW"))
  2831.         {   if (MatchToolValue(s, "JOY"))
  2832.             {   worm[3].control = JOYSTICK;
  2833.             } elif (MatchToolValue(s, "GAMEPAD"))
  2834.             {   worm[3].control = GAMEPAD;
  2835.             } elif (MatchToolValue(s, "AMIGA"))
  2836.             {   worm[3].control = AMIGA;
  2837.             } elif (MatchToolValue(s, "NONE"))
  2838.             {   worm[3].control = NONE;
  2839.         }   }
  2840.         FreeDiskObject(DiskObject);
  2841. }   }
  2842.  
  2843. MODULE void pausetimer(void)
  2844. {    GetSysTime(CurrentValPtr);
  2845. }
  2846. MODULE void unpausetimer(void)
  2847. {    GetSysTime(PausedValPtr);
  2848.     SubTime(PausedValPtr, CurrentValPtr);
  2849.     AddTime(StartValPtr, PausedValPtr);
  2850. }
  2851.  
  2852. MODULE UBYTE ReadJoystick(UWORD joynum)
  2853. {   extern struct Custom far custom;
  2854.     UBYTE ret = 0;
  2855.     UWORD joy;
  2856.  
  2857.     if (joynum == 0)
  2858.         joy = custom.joy0dat;
  2859.     else joy = custom.joy1dat;
  2860.  
  2861.     ret += (joy >> 1 ^ joy) & 0x0100 ? JOYUP : 0;  
  2862.     ret += (joy >> 1 ^ joy) & 0x0001 ? JOYDOWN : 0;
  2863.     ret += joy & 0x0200 ? JOYLEFT : 0;
  2864.     ret += joy & 0x0002 ? JOYRIGHT : 0;
  2865.  
  2866.     if (joynum == 0)
  2867.     {   ret += !(CIAPtr->ciapra & 0x0040) ? JOYFIRE1 : 0; /* read firebuttons */
  2868.         ret += !(POTGOR & 0x0400) ? JOYFIRE2 : 0;         /* on joyport 0 */
  2869.     } else
  2870.     {   ret += !(CIAPtr->ciapra & 0x0080) ? JOYFIRE1 : 0; /* read firebuttons */
  2871.         ret += !(POTGOR & 0x0400) ? JOYFIRE2 : 0;         /* on joyport 1 */
  2872.     }
  2873.  
  2874.     return(ret);
  2875. }
  2876.  
  2877. AGLOBAL void ReadAdapterJoystick(UBYTE unit)
  2878. {   AUTO    UBYTE firebit, addroffset, snapshot;
  2879.     AUTO    SBYTE xx = 0, yy = 0;
  2880.     AUTO    ABOOL fire           = FALSE;
  2881.     PERSIST UBYTE oldsnapshot[2] = {0, 0};
  2882.     PERSIST ABOOL oldfire[2]     = {0, 0};
  2883.     PERSIST UBYTE *fireaddrPtr,
  2884.                   *moveaddrPtr;
  2885.  
  2886.     fireaddrPtr = 0xBFD000;
  2887.     moveaddrPtr = 0xBFE101;
  2888.  
  2889. /*  Those pointers could easily be auto or constant or whatever; they
  2890.     never change.
  2891.  
  2892.     bit 7 of $bfe101 is right of joystick '4'
  2893.     bit 6 of $bfe101 is left  of joystick '4'
  2894.     bit 5 of $bfe101 is down  of joystick '4'
  2895.     bit 4 of $bfe101 is up    of joystick '4'
  2896.     bit 3 of $bfe101 is right of joystick '3'
  2897.     bit 2 of $bfe101 is left  of joystick '3'
  2898.     bit 1 of $bfe101 is down  of joystick '3'
  2899.     bit 0 of $bfe101 is up    of joystick '3'
  2900.  
  2901.     bit 2 of $bfd000 is fire  of joystick '3'
  2902.     bit 0 of $bfd000 is fire  of joystick '4'
  2903.  
  2904.     unit: number of the unit (2 = '3', 3 = '4') */
  2905.  
  2906.     if (unit == 2)
  2907.     {   firebit = 2;
  2908.         addroffset = 0;
  2909.     } else
  2910.     {   // assert(unit == 3);
  2911.         firebit = 0;
  2912.         addroffset = 4;
  2913.     }
  2914.  
  2915.     if (worm[unit].control == JOYSTICK && worm[unit].lives)
  2916.     {   if ((*fireaddrPtr) & (1 << firebit))
  2917.             fire = TRUE;
  2918.  
  2919.         snapshot = *moveaddrPtr; // we cache this address in the snapshot
  2920.                                  // variable, so that joystick reading is an
  2921.                                  // atomic operation
  2922.         if (snapshot & (1 << (addroffset + 3)))
  2923.             xx = 1;
  2924.         elif (snapshot & (1 << (addroffset + 2)))
  2925.             xx = -1;
  2926.         if (snapshot & (1 << (addroffset + 1)))
  2927.             yy = 1;
  2928.         elif (snapshot & (1 <<  addroffset     ))
  2929.             yy = -1;
  2930.  
  2931.         if (oldsnapshot[unit - 2] != snapshot || oldfire[unit - 2] != fire)
  2932.         {   if (fire)
  2933.                 wormqueue(unit, 0, 0);
  2934.             else
  2935.                 wormqueue(unit, xx, yy);
  2936.         }
  2937.         oldsnapshot[unit - 2] = snapshot;
  2938.         oldfire[unit - 2]     = fire;
  2939. }   }
  2940.  
  2941. MODULE ABOOL firebutton(void)
  2942. {   UBYTE PortState = ReadJoystick(1);
  2943.  
  2944.     if (PortState & JOYFIRE1) // don't check JOYFIRE2
  2945.     {   return(TRUE);
  2946.     } else return(FALSE);
  2947. }
  2948.  
  2949. AGLOBAL void ReadStandardJoystick(ULONG port)
  2950. {   AUTO    UBYTE PortState[2];
  2951.     AUTO    ABOOL fire            = FALSE;
  2952.     AUTO    SBYTE xx              = 0,
  2953.                   yy              = 0,
  2954.                   player;
  2955.     PERSIST UBYTE OldPortState[2] = {0, 0};
  2956.  
  2957.     if (port == 1)
  2958.     {   player = 2; // blue worm
  2959.     } else
  2960.     {   // assert(port == 0);
  2961.         player = 3; // yellow worm
  2962.     }
  2963.     if (worm[player].control == JOYSTICK && worm[player].lives)
  2964.     {   PortState[port] = ReadJoystick(port);
  2965.         if (PortState[port] != OldPortState[port])
  2966.         {   if (PortState[port] & JOYUP)
  2967.                 yy = -1;
  2968.             elif (PortState[port] & JOYDOWN)
  2969.                 yy = 1;
  2970.             if (PortState[port] & JOYLEFT)
  2971.                 xx = -1;
  2972.             elif (PortState[port] & JOYRIGHT)
  2973.                 xx = 1;
  2974.             if (PortState[port] & JOYFIRE1)
  2975.             {   fire = TRUE;
  2976.             }
  2977.  
  2978.             // this doesn't work perfectly?
  2979.             if (PortState[port] != 0 && (worm[player].brakes || xx == 0 || yy == 0))
  2980.             // if joystick is off-centre or firebutton is down
  2981.             {   if (fire)                      // if firebutton is down
  2982.                 {   wormqueue(player, 0, 0);   // then shoot/jump
  2983.                 } else                         // if firebutton is up
  2984.                 {   wormqueue(player, xx, yy); // then move
  2985.         }   }   }
  2986.         OldPortState[port] = PortState[port];
  2987. }   }
  2988.  
  2989. void flash(ULONG where)
  2990. {   TEXT saystring[SAYLIMIT + 1];
  2991.  
  2992.     stci_d(saystring, where);
  2993.     say(saystring, PURPLE);
  2994.     while(1);
  2995. }
  2996.  
  2997. AGLOBAL void ReadGamepads(void)
  2998. {   AUTO    ULONG PortState;
  2999.     AUTO    ABOOL fire = FALSE;
  3000.     AUTO    SBYTE i, xx = 0, yy = 0;
  3001.     PERSIST ULONG OldPortState = 0;
  3002.  
  3003.     for (i = 0; i <= 3; i++)
  3004.     {   if (worm[i].control == GAMEPAD && worm[i].lives)
  3005.         {   PortState = ReadJoyPort(worm[i].port);
  3006.             if (PortState != OldPortState)
  3007.             {   if (PortState & JP_BUTTON_MASK)
  3008.                 {   fire = TRUE;
  3009.                 }
  3010.                 if (PortState & JPF_JOY_UP)
  3011.                 {   yy = -1;
  3012.                 } elif (PortState & JPF_JOY_DOWN)
  3013.                 {   yy = 1;
  3014.                 }
  3015.                 if (PortState & JPF_JOY_LEFT)
  3016.                 {   xx = -1;
  3017.                 } elif (PortState & JPF_JOY_RIGHT)
  3018.                 {   xx = 1;
  3019.                 }
  3020.                 if (fire)                 // if firebutton is down
  3021.                 {   wormqueue(i, 0, 0);   // then shoot/jump
  3022.                 } elif (xx || yy)
  3023.                 {   wormqueue(i, xx, yy);
  3024.             }   }
  3025.             OldPortState = PortState;
  3026. }   }   }
  3027.  
  3028.